瀑布流,又稱瀑布流式布局。是比較流行的一種網站頁面布局,視覺表現為參差不齊的多欄布局,隨着頁面滾動條向下滾動,這種布局還會不斷加載數據塊並附加至當前尾部。
1、首先瀑布流所有的圖片應該保持寬度一致,高度是由內容決定。
通過定位的方式是我們實現瀑布流的最基本的原理,只要我們動態的設置它的top
值、left
值,就能讓它排列。
2、定位后確定瀏覽器顯示區域內,一行能放多少列圖片盒子。
- 獲取頁面的寬度
- 獲取圖片盒子的寬度
- 顯示的列數 = 頁面寬度/圖片盒子寬度
column = pageWidth / itemWidth
3、為了美觀我們可以加上一個空隙
- 顯示的列數 = 頁面寬度/(圖片盒子寬度+間隙);
column = pageWidth / (itemWidth + gap);
4、 確定列數之后,排列第一行
- 下面還有很多圖片盒子,我們先要排列第
1
行,所以在for
循環里就要判斷一下,當i
(所有圖片盒子的索引) <column
(顯示列數)的時候,說明在第1
行; - 知道在第
1
行之后,動態設置每個圖片盒子的left
值就能排好第1
行。 left = i * ( itemWidth + gap );
5、第1行排列好之后,獲取第1行所有圖片盒子的高度
- 需要定義一個數組
arr
,將獲取到的高度存在數組中,因為第2
行排列的時候需要考慮top
值,此時只能根據第1
行圖片盒子的高度來設置; - 獲取圖片高度的時候要注意,程序必須寫在入口函數
onload
里面,因為圖片的加載特性是:等頁面都加載完之后才去請求加載,所以不寫在入口函數里可能會出現高度獲取不到的情況。
6、排列第2行
- 獲取到剛剛數組中,高度最小的那一列,將第
2
行的第1
個圖片盒子放置在它的下方; - 此時的
left
值就是高度最小列的offsetLeft
;top
值就是:第1
行高度最小列的高度(為了布局美觀可以加上上下間隙gap
)。 - 記錄下高度最小列的索引
index
,后面計算會用到; - 設置完成之后,會發現后面所有的圖片都疊在這個高度最小列的下面,原因就是此時的最小列高度沒有改變,應該加上下面圖片的高度,得出一個新高度。
7、改變最小列當前高度
- 此時的這一列高度其實已經發生改變了,所以需要將新高度賦值給數組
- 當前高度最小列的高度 = 當前高度最小列的高度 + 間隙 + 下面圖片盒子的高度
8、觸發resize事件
- 將整個設置樣式的部分封裝成一個函數,在
onload
里面注冊一個resize
事件,只要頁面一發生改變,就觸發樣式部分的代碼。 - 實時改變
pageWidth
的寬度,這樣瀑布流就會是一個響應式的效果了
9、懶加載效果
- 目前我們用的是
30
張圖片,假如一個頁面中有幾百張圖片的時候,我們不可能等到它都加載完再顯示,所有這里引入一個懶加載的概念,我們規定第30
張為顯示的最后一張圖片,當滾動條滾動到30
張的時候,應該加載下一批圖片。 - 即頁面可視區高度+滾動條卷去的高度 = 第
30
圖片的offsetTop
;的時候加載下面的圖片。
代碼:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>瀑布流</title> <link rel="stylesheet" type="text/css" href="../css/flow.css"> <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script> <script type="text/javascript" src="../js/script.js"></script> </head> <body> <div id="main"> <div class="box"> <div class="pic"> <img src="../img/food-list1.png"> </div> </div> <div class="box"> <div class="pic"> <img src="../img/food-list2-1.png"> </div> </div> . . . <div class="box"> <div class="pic"> <img src="../img/food-list2-2.png"> </div> </div> <div class="box"> <div class="pic"> <img src="../img/food-list2-3.png"> </div> </div> </div> </body> </html>


window.onload=function(){ waterfall("main","box"); var dataInt={"data":[{"src":"food-list2-1.png"},{"src":"food-list2-2.png"},{"src":"food-list2-3.png"},{"src":"food-list2-4.png"},{"src":"food-list1.png"}]}; window.onscroll=function(){ if(checkScrollSlide()){ var oParent=document.getElementById('main'); for(let i=0;i<dataInt.data.length;i++){ var oBox=document.createElement("div"); oBox.className='box'; oParent.appendChild(oBox); var oPic=document.createElement("div"); oPic.className="pic"; oBox.appendChild(oPic); var oImg=document.createElement("img"); oImg.src="../img/"+dataInt.data[i].src; oPic.appendChild(oImg); } waterfall("main","box"); } } } function waterfall(parent,box){ //獲取main下所有class為box的元素 var oParent=document.getElementById(parent); var oBoxs=getByClass(oParent,box); //獲取列數 var oBoxW=oBoxs[0].offsetWidth; // console.log(document.documentElement.clientWidth) var cols=parseInt(screen.availWidth/oBoxW); oParent.style.cssText="width:"+oBoxW*cols+"px;margin:0 auto" var hArr=[];//存放每列圖片高度 for(let i=0;i<oBoxs.length;i++){ if(i<cols){ hArr.push(oBoxs[i].offsetHeight) }else{ var minH=Math.min.apply(null,hArr); var index=getMinhIndex(hArr,minH); oBoxs[i].style.position='absolute'; oBoxs[i].style.top=minH+'px'; oBoxs[i].style.left=oBoxW*index+'px'; hArr[index]+=oBoxs[i].offsetHeight; } } } //根據class獲取元素 function getByClass(parent,clsName){ var boxArr=[]; var oElements=parent.getElementsByTagName('*'); for(let item of oElements){ if(item.className==clsName){ boxArr.push(item) } } return boxArr; } function getMinhIndex(arr,val){ for (var i in arr) { if(arr[i]==val) return i; } } //檢測是否具備滾動加載數據塊的條件 function checkScrollSlide(){ var oParent=document.getElementById('main'); var oBoxs=getByClass(oParent,'box'); var lastBoxH=oBoxs[oBoxs.length-1].offsetTop+parseInt(oBoxs[oBoxs.length-1].offsetHeight); var scrollTop=document.documentElement.scrollTop||document.body.scrollTop; var totalH=scrollTop+document.documentElement.clientHeight; return (lastBoxH<=totalH)?true:false; }
效果圖: