摘要:一一些閑話作為一個寫靜態的切圖仔,其實日常工作中根本用不上瀑布流這種小清新,畢竟營銷頁面都是要求搶眼吸睛高大上文案爸爸說啥都對。昨上午閑著沒事看到別人寫的瀑布流的帖子,覺得很好玩的樣子,然后決定上午就寫一個試試。。。
一、一些閑話
二、需求整理及最終效果作為一個寫靜態的切圖仔,其實日常工作中根本用不上瀑布流這種小清新,畢竟營銷頁面都是要求 搶眼__、__吸睛__、 __高大上 (文案爸爸說啥都對)。
昨上午閑著沒事看到別人寫的瀑布流的帖子,覺得很好玩的樣子,然后決定上午就寫一個試試。。。所以,今天下午,就來整理下這過程中的一些思路。
寫代碼之前大概列了一下需求,然后中間又加上了一些其他功能,最終的需求如下:
2.1 需求列表1、希望是用原生 js 代碼,jq 寫多了怕忘了原生;
2、面對對象方式封裝,根據圖片數據渲染頁面;
3、瀑布流部分可以添加至任意容器元素內,畢竟頁面還會有其他內容;
4、圖片寬度,行列間距可定義;
5、圖片外容器可以自定義邊框、陰影等屬性;
6、圖片數據增加后可以調用方法渲染新增部分,原始部分保存不變;
2.2 最終效果完整代碼
頁面預覽
3秒后新增3張圖片
三、代碼實現 3.1 基礎框架起一個自執行函數,只需要暴露一個 falls 變量,該變量指向一個包含 init 方法的對象;
init 方法有2個參數:
- el:瀑布流的容器的選擇符 - options:其他參數
ps: 這里還用到了一個自定義的 extend 方法,用于合并默認屬性以及自定義屬性對象,怕被說兼容性不好就沒有用 ES6 語法,參照 Object.assign 方法,完整代碼里也有,此處不多介紹;
var falls = (function () { var defaults = { }; var Falls = function (el, options) { }; var prototype = Falls.prototype; var init = function (el, options) { options = extend([], defaults, options); return new Falls(el, options).init(); }; return { init: init } })();3.2 基礎屬性
接下來確定一些可以定義的屬性:
- width: 圖片(圖片外容器)的寬度 - colSpace: 列間距 - rowSpace: 行間距 - itemClass: 圖片外容器類名,方便修改邊框、陰影等樣式
根據這些屬性,隨手確定了各項默認值
var defaults = { width: 220, colSpace: 10, rowSpace: 10, itemClass: "_list-item" };3.3 構造函數
定義 Falls 構造函數,最終該構造函數有以下初始屬性(屬性在后面用到再做解釋):
var Falls = function (el, options) { this.el = document.querySelector(el); this.imgList = options.imgList; this.colSpace = options.colSpace; this.rowSpace = options.rowSpace; this.width = options.width; this.itemClass = options.itemClass; this.first = true; this.startIndex = 0; this.callback = []; this.loadAll = false; };3.4 添加原型方法
在添加原型方法之前:
var prototype = Falls.prototype;
這樣可以少寫好多字母呢,真棒!!!
3.4.1 初始化 initialize 方法prototype.initialize = function () { var rootEl = document.createElement("div"); rootEl.style.margin = "0 auto"; rootEl.style.position = "relative"; this.rootEl = rootEl; this.el.appendChild(this.rootEl); };
這里定義了根元素 rootEl ,并給它添加了相對定位及 margin 屬性,這樣整個根元素會在容器中水平居中。
3.4.2 瀑布流加載 loadFalls 方法這里有一些前置屬性及初始邏輯
prototype.loadFalls = function () { var wrapWidth = this.el.clientWidth; // 獲取容器的寬度 this.colWidth = this.width + this.colSpace; // 單個圖片加上列間隙需要的寬度 this.col = Math.floor((wrapWidth + this.colSpace) / this.colWidth); // 獲取圖片列數 this.rootEl.style.width = this.col * this.colWidth - this.colSpace + "px"; // 根元素的寬度 if (this.first) { // 如果初次渲染,直接執行 this.storageTop(); this.addItem(); this.first = false; this.lastCol = this.col; } else { // 非初次渲染,判斷列數是否變化 if (this.lastCol !== this.col) { this.startIndex = 0; // 列數變化時,全部重新渲染 this.rootEl.innerHTML = ""; // 清空根元素 this.storageTop(); this.addItem(); this.lastCol = this.col; } } };
以代碼空行來拆分:
第一部分:__屬性定義__,見注釋。
第二部分:__邏輯部分__
如果是第一次渲染,就依次初始化,執行 addItem 添加圖片列表,然后標記 this.first 為 false,并且記錄當前的列數 this.lastCol
非第一次渲染,只有列數改變時才重新渲染瀑布流,this.startIndex 是圖片數組的標記位,后面會講到。
3.4.3 生成高度列表 storageHeight 方法prototype.storageTop = function () { var topArr = []; for (var i = 0; i < this.col; i++) { topArr.push({ left: this.colWidth * i, top: 0 }) } this.topArr = topArr; };
根據列數生成一個存儲每列下一張圖片 top 及 left 值的數組,top 初始都為 0 ,left 為每列的寬度 * 列數;
3.4.4 添加圖片隊列 addItem 方法prototype.addItem = function () { var _this = this, maxHeight = 0, topArr = this.topArr, imgList = this.imgList.slice(_this.startIndex), len = topArr.length; (function addImg() { var current = imgList.shift(), top = topArr[0].top, index = 0; for (var j = 1; j < len; j++) { // 遍歷求出當前最小 top 值,及對應的列數 index if (topArr[j].top < top) { top = topArr[j].top; index = j; } } var item = document.createElement("div"); // 創建圖片包裹元素 item.style.position = "absolute"; item.style.top = top + "px"; item.style.left = topArr[index].left + "px"; item.style.width = _this.width + "px"; item.style.boxSizing = "border-box"; item.classList.add(_this.itemClass); var img = document.createElement("img"); // 創建圖片元素 img.style.width = "100%"; img.src = current.src; img.alt = current.alt; item.appendChild(img); _this.rootEl.appendChild(item); img.onload = function () { topArr[index].top += item.offsetHeight + _this.rowSpace; // 新增圖片后更新高度數組 maxHeight = maxHeight < topArr[index].top ? topArr[index].top : maxHeight; _this.rootEl.style.height = maxHeight + "px"; // 更新容器的高度 if (imgList.length) { addImg(); } else { _this.startIndex = _this.imgList.length; if (!_this.callback.length) { _this.loadAll = true; } else { _this.callback.shift()(); } } }; })(); };
這一塊有點長,因為有兩處為 dom 對象添加屬性,主要邏輯如下:
1、變量聲明,保存 this ,復制圖片數組至 imgList(因為后面會對數組進行更改);
2、創建 addImg 方法添加單個圖片進根元素,在圖片的 onload 事件里遞歸 addImg 添加下一張圖片;
只有在圖片加載完成后才能獲取圖片高度,進行 topArr 的更新
3、在 addImg 函數內,首先遍歷出當前最小 top 值,及對應的列數 index;
4、生成圖片容器元素 item ,并添加屬性及暴露的類名 itemClass;
5、生成圖片元素 img ,并添加屬性,圖片寬度100%;
6、依次添加圖片及圖片容器至根元素,注意先后順序;
7、進入 onload 事件內,首先需要更新 topArr 對應序號的 top 值,因為該位置新增了一張圖片
8、求出總高度更新根元素高度(防止根元素后面其他頁面元素布局混亂);
9、如果 imgList 內還有數據,遞歸完成圖片添加
10、如果 imgList 無數據:
3.4.5 監聽寬度變化 bindEvent 方法記錄圖片索引至 startIndex ,新增圖片數據后只需從 startIndex 位置開始添加
檢查回調隊列 callback 內是否有回調事件,如果有,取出第一條進行處理(回調隊列后面解釋)
prototype.bindEvent = function () { // 通過 resize 事件監聽容器寬度變化 window.addEventListener("resize", this.loadFalls.bind(this)); };
為 window 對象的 resize 事件添加監聽,觸發 loadFalls 函數,這里通過bind修正了方法內部的 this 指向;
3.4.6 生成一個瀑布流實例 init 方法prototype.init = function () { // 生成一個瀑布流實例 this.initialize(); this.loadFalls(); this.bindEvent(); return this; // 返回實例對象 };
就是依次調用 初始化 添加圖片 綁定事件 三個方法,這里返回了 this ,用于保存當前實例,下一步會用到;
3.4.7 添加圖片后重新繪制 addImgReload 方法prototype.addImgReload = function (arr) { var _this = this; if (this.loadAll) { this.imgList = arr; this.addItem(); } else { this.callback.push(function () { _this.imgList = arr; _this.addItem(); }) } };
上面為了這一步做了很多鋪墊
四、總結this.loadAll 保存當前圖片隊列是否全部加載完成
如果當前圖片隊列已經加載完成,那就跟新圖片隊列 this.imgList ,繼續加載 this.addItem(),因為已經存儲了 startIndex ,所以會從新增的圖片繼續加載
如果當前圖片隊列還沒加載完成,將更新圖片的任務推進回調隊列 callback ,當前圖片隊列加載完成后會檢測回調隊列,取出更新圖片任務完成,就算有多個圖片更新事件也不要緊, callback 保持先進先出執行順序;
似乎沒有提懶加載
通過 addImgReload 方法分次跟新圖片屬性可以實現懶加載
圖片數據可以根據實際擴充,此處只添加了 src 及 alt ,包括超鏈接可以修改外層容器 item 或者再套一層;
代碼寫完沒有做優化,有幾段比較長,有空再優化吧,畢竟快下班了
最后,個人能力有限,歡迎大佬補充,謝謝!!!
編輯文章的時候發現了一個坑,圖片加載失敗會阻塞后續圖片,明天改
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/99970.html
摘要:使用實現瀑布流并不實用,因為實現的瀑布流都是以列來排列的,這里記錄下用實現瀑布流,以及微信小程序中使用左右兩列來實現瀑布流效果圖原生實現瀑布流文件圖片可以自己找點替換下就可以了文件添加陰影的時候,加上會顯得更加有點懸浮感文件計算圖片列數 使用css實現瀑布流并不實用,因為css實現的瀑布流都是以列來排列的,這里記錄下用js實現瀑布流,以及微信小程序中使用左右兩列來實現瀑布流 1.效果圖...
摘要:前言最近在整理基礎知識,接觸到了幾個常用的頁面特效,其中覺得用原生實現瀑布流的案例十分有趣,于是與大家分享一下。瀑布流瀑布流,又稱瀑布流式布局。通過定位的方式是我們實現瀑布流的最基本的原理,只要我們動態的設置它的值值,就能讓它排列。 showImg(https://segmentfault.com/img/remote/1460000012621941?w=1052&h=542); 前...
摘要:瀑布流布局中的圖片有一個核心特點等寬不定等高,瀑布流布局在國內網網站都有一定規模的使用,比如花瓣網等等。那么接下來就基于這個特點開始瀑布流探索之旅。 showImg(https://segmentfault.com/img/remote/1460000013059759?w=640&h=280); 瀑布流布局中的圖片有一個核心特點 —— 等寬不定等高,瀑布流布局在國內網網站都有一定規模...
摘要:瀑布流布局中的圖片有一個核心特點等寬不定等高,瀑布流布局在國內網網站都有一定規模的使用,比如花瓣網等等。那么接下來就基于這個特點開始瀑布流探索之旅。 showImg(https://segmentfault.com/img/remote/1460000013059759?w=640&h=280); 瀑布流布局中的圖片有一個核心特點 —— 等寬不定等高,瀑布流布局在國內網網站都有一定規模...
閱讀 2009·2021-11-24 09:39
閱讀 1878·2019-08-30 15:55
閱讀 2168·2019-08-30 15:53
閱讀 565·2019-08-29 13:16
閱讀 983·2019-08-26 12:20
閱讀 2379·2019-08-26 11:58
閱讀 3129·2019-08-26 10:19
閱讀 3296·2019-08-23 18:31