js+CSS簡單實(shí)現(xiàn)瀑布流布局
前言
瀑布流布局,是一種視覺表現(xiàn)為參差不齊的多欄布局。常用于內(nèi)容以圖片為主的頁面展示,圖片等寬不等高,每行從左到右排滿后,后面的元素依次添加至短的一列其后。隨著頁面向下滾動(dòng),還會(huì)不斷加載數(shù)據(jù)并添加到當(dāng)前尾部,視覺上顯得錯(cuò)落有致不拘一格。今天使用css和js兩種方式來實(shí)現(xiàn)瀑布流布局
css實(shí)現(xiàn)瀑布流布局
該方式實(shí)現(xiàn)的瀑布流布局其中的內(nèi)容數(shù)據(jù)是固定的,主要利用了 CSS 的多列屬性 columns
頁面結(jié)構(gòu):創(chuàng)建容器container,該容器包裹所有用于展示的元素item(圖片和標(biāo)題)
<div class="container"> <div class="item"> <img src="..."/> <h2>...</h2> </div> ... </div>
給容器container設(shè)置寬度,并居中顯示;控制其中的內(nèi)容分成3列columns: 3;
,列與列之間的距離為20pxcolumn-gap: 20px;
每個(gè)item元素,設(shè)置屬性break-inside: avoid;
, 避免在多列布局中元素內(nèi)容被分割(元素的內(nèi)容被自動(dòng)斷開,圖片在一列尾部,文字在下一列的頭部)
.container { width: 1200px; margin: 20px auto; columns: 3; column-gap: 20px; } .container .item { width: 100%; break-inside: avoid; margin-bottom: 20px; } .container .item img{ width: 100%; }
小結(jié):根據(jù)上圖可以看出CSS的多列布局實(shí)現(xiàn)的瀑布流是從上到下,再從左到右的順序排列元素,適用于對(duì)于元素順序沒有要求,且元素?cái)?shù)據(jù)固定的情況
js實(shí)現(xiàn)瀑布流布局
思路:
每個(gè)元素使用絕對(duì)定位的方式,動(dòng)態(tài)的設(shè)置相應(yīng)的top,left值進(jìn)行排列,先計(jì)算一行能夠容納幾列元素,第一行元素直接設(shè)置top為0,left為索引乘以(元素寬度 + 間隙)進(jìn)行排列,后面的元素排列,需要找出所有列中高度最小的那一列,將元素添加至高度最小的那一列后面,直至所有元素添加完畢。
創(chuàng)建一個(gè)容器container,用于存放展示的元素。并設(shè)置css為相對(duì)定位,其中展示的元素為絕對(duì)定位
<div id="container"></div>
計(jì)算頁面一行能容納幾列元素
列數(shù) = 頁面的寬度 / (元素的寬度+元素之間的間隙 ) column = pageWidth / (itemWidth + gap);
注:頁面的寬度這里設(shè)置的是body的寬度(使用css控制了最大寬度和最小寬度)
創(chuàng)建createElement函數(shù),用來動(dòng)態(tài)創(chuàng)建組成瀑布流的元素塊(包含圖片和標(biāo)題),并在請(qǐng)求數(shù)據(jù)成功后調(diào)用添加到頁面中,并進(jìn)行瀑布流布局
function createElement(itemData) { return new Promise((resolve, reject) => { let div = document.createElement('div') div.innerHTML = `...` div.className = 'item' // 監(jiān)聽圖片的load事件 div.querySelector('img').addEventListener('load', () => { resolve(div) }) }) } // 獲取喵咪的數(shù)據(jù) let page = 1 //請(qǐng)求數(shù)據(jù)的頁碼 let isReq = false //設(shè)置節(jié)流閥,處于請(qǐng)求過程中,則不再請(qǐng)求數(shù)據(jù) function loadNewData() { if (isReq) return isReq = true getData() .then(data => { // 處理新的數(shù)據(jù) let items = data let promises = items.map(item => createElement(item)) Promise.all(promises).then(nodes => { let frag = document.createDocumentFragment() isReq = false page++ nodes.forEach(node => { frag.appendChild(node) Node.push(node) }) document.getElementById('container').appendChild(frag) waterfall(Node) }) }) }
注:遇到獲取元素塊高度不正確的問題
因?yàn)閳D片可能還沒有完全加載,獲取的元素塊高度就只是h4標(biāo)簽的高度。因此需要在圖片加載完成后再獲取元素的高度。
a.通過監(jiān)聽圖片的 load
事件,當(dāng)事件觸發(fā)時(shí),表示圖片已經(jīng)加載完成,返回創(chuàng)建的div
元素
b.在獲取數(shù)據(jù)loadNewData()
函數(shù)中,對(duì)返回的數(shù)據(jù)執(zhí)行創(chuàng)建元素方法,并使用Promise.all()
對(duì)由返回的div
元素(Promise
實(shí)例)組成的Promise
數(shù)組進(jìn)行處理,當(dāng)每個(gè)Promise
實(shí)例的狀態(tài)變?yōu)?fulfilled
(所有圖片都加載完成),就執(zhí)行瀑布流布局
創(chuàng)建waterfall函數(shù),實(shí)現(xiàn)瀑布流
接收元素?cái)?shù)組,然后對(duì)每個(gè)元素進(jìn)行布局。如果元素在第一行,那么它的頂部位置就是 0,左側(cè)位置就是它的索引乘以(元素寬度 + 間隙)。如果元素不在第一行,那么我們首先找出高度最小的列,然后將元素放在這個(gè)列的下方,并更新這個(gè)列的高度。
function waterfall(items) { for (var i = loaded; i < items.length; i++) { let item = items[i] let height = item.offsetHeight if (i < columns) { //滿足這個(gè)條件則說明在第一行 item.style.top = 0 item.style.left = (itemWidth + gap) * i + 'px' heightArr.push(height) } else { //其他行,先找出最小高度列的索引 const minIndex = getMinIndex(heightArr) //top值就是最小列的高度+gap const top = heightArr[minIndex] + gap item.style.top = top + 'px' item.style.left = minIndex * (itemWidth + gap) + 'px' //修改原本最小列的高度 heightArr[minIndex] = top + height } loaded = items.length } } //用于獲取heightArr數(shù)組中值最小的列的索引。 const getMinIndex = (array) => { const min = Math.min.apply(null, array) return array.indexOf(min) }
監(jiān)聽頁面滾動(dòng),當(dāng)未顯示的元素內(nèi)容小于屏幕高度的一大半(這里設(shè)置的0.7,因?yàn)閿?shù)據(jù)加載比較慢,一般設(shè)置成0.5就可以),就需要加載新數(shù)據(jù)進(jìn)行展示
window.addEventListener('scroll', lazy) function lazy() { const scrollTop = document.documentElement.scrollTop || document.body.scrollTop const documentHeight = document.documentElement.scrollHeight // 未顯示的內(nèi)容 = 文檔(內(nèi)容)高度-滾動(dòng)高度-屏幕高度 if (documentHeight - scrollTop - clientHeight < 0.7 * clientHeight) { loadNewData() } }
小結(jié):
a. 簡單封裝的請(qǐng)求數(shù)據(jù)方法getData()沒有展示出來,請(qǐng)求的接口是從網(wǎng)上找的(地址),一次只能加載10張,并且加載很慢,可以自行查找接口。
b. 需要定義的變量
根據(jù)以上基本可以實(shí)現(xiàn)瀑布流了,排列順序和滾動(dòng)頁面加載數(shù)據(jù)也比較符合一般情況
以上就是js+CSS簡單實(shí)現(xiàn)瀑布流布局的詳細(xì)內(nèi)容,更多關(guān)于js瀑布流布局的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
利用javascript的面向?qū)ο蟮奶匦詫?shí)現(xiàn)限制試用期
Javascript是一種面向?qū)ο蟮哪_本語言,其也具有面向?qū)ο蟮娜筇匦?,但是今天我們不詳?xì)的講解javascript的面向?qū)ο筇匦裕裉煳覀兒唵蔚牧私庖幌耲avascript的面向?qū)ο筇匦?,然后學(xué)習(xí)一下怎樣實(shí)現(xiàn)試用期的限制!2011-08-08js從10種顏色中隨機(jī)取色實(shí)現(xiàn)每次取出不同的顏色
昨天在做js 從10種顏色中隨機(jī)取色,并每次取出的顏色不同,具體的實(shí)現(xiàn)思路如下,感興趣的朋友可以參考下2013-10-10javascript仿163網(wǎng)盤無刷新文件上傳系統(tǒng)
這個(gè)仿163網(wǎng)盤無刷新文件上傳系統(tǒng),并沒有用使用.net的控件,完全的手工制作。2008-10-10javascript css紅色經(jīng)典選項(xiàng)卡效果實(shí)現(xiàn)代碼
這篇文章主要為大家詳細(xì)介紹了javascript css紅色經(jīng)典選項(xiàng)卡效果的實(shí)現(xiàn)代碼,需要的朋友可以參考下2016-05-05