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