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