淺析瀑布流佈局及其原理
阿新 • • 發佈:2019-01-01
一、什麼是瀑布流
很多時候我們會看到一些Vlog網站或者展示圖片的網站,它們的圖片明明每一張的高度大小都不同,但是卻能自動地適應,排成一行一行地展示,並且下拉到底的時候,載入的圖片也會自動適應,這就是瀑布流,比如下圖。
二、實現原理
要做到每一張圖片都根據上面的高度自動適應排列,那麼我們就不能單純地靠html+css佈局了,需要用到js來幫助計算位置(其實用CSS3也能佈局)。那麼計算什麼呢?
首先,我們需要理清一個思路,就是這個佈局是按一列列來看的,如下圖:
我們要做的,其實就是在每一列下面插入新的圖片,這樣它就會緊挨著上面的圖片對齊。至於上面和下面的圖片間距,那麼很自然地是利用了margin和padding屬性,不熟悉盒子屬性的可以移步去 理解padding和margin,等於理解了盒子模型這篇文章看看。
三、事先準備
- 建議事前在網上隨便下載15張以上的圖片,不用理會長寬問題,這些都是可以用css設定的;
- 準備好jQuery
- 然後按照以下佈局去把HTML結構和CSS樣式寫好:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <link rel="stylesheet" href="css/index.css"> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script> <script src="js/index.js"></script> <title>Document</title> </head> <body> <div id="wrap" class="wrap"> <div class="item"> <img src="images/1.jpg" alt=""> </div> <div class="item"> <img src="images/2.jpg" alt=""> </div> <div class="item"> <img src="images/3.jpg" alt=""> </div> <div class="item"> <img src="images/4.jpg" alt=""> </div> <div class="item"> <img src="images/5.jpg" alt=""> </div> <div class="item"> <img src="images/6.jpg" alt=""> </div> <div class="item"> <img src="images/7.jpg" alt=""> </div> <div class="item"> <img src="images/8.jpg" alt=""> </div> <div class="item"> <img src="images/9.jpg" alt=""> </div> <div class="item"> <img src="images/10.jpg" alt=""> </div> <div class="item"> <img src="images/11.jpg" alt=""> </div> <div class="item"> <img src="images/12.jpg" alt=""> </div> <div class="item"> <img src="images/13.jpg" alt=""> </div> <div class="item"> <img src="images/14.jpg" alt=""> </div> <div class="item"> <img src="images/15.jpg" alt=""> </div> <div class="item"> <img src="images/16.jpg" alt=""> </div> <div class="item"> <img src="images/17.jpg" alt=""> </div> <div class="item"> <img src="images/18.jpg" alt=""> </div> </div> </body> </html>
css樣式:
*{ margin: 0; padding: 0; border:none } body{ background: #ddd } .wrap{ width: auto; height: auto; position: relative; margin:0 auto; } .item{ float: left; width: 280px; height: auto; padding: 10px; margin: 10px; box-sizing: border-box; } .item img{ display: block; width:100%; border-radius: 10px; cursor: pointer; } .item img:hover{ background-color: gray; opacity: 0.5; }
至此,那麼你的第一排就已經佈局好了,接下來我們就是要用js去計算一頁能有多少列圖片以及如何在每一列裡面插入新圖片。
四、瀑布流的核心
實現瀑布流的核心其實就兩個:
- 找出圖片高度最小的那一列,再那一列插入,然後繼續找下一個高度最小的,一直迴圈直到插滿圖片為止;
- 計算出每一列距離瀏覽器整體的距離,也就是
position
裡的left
或right
,有什麼用呢?當你知道某一列的left
的時候,相當於就知道了在它下面插入圖片時,圖片如何定位到這一列了,只要圖片的left
值和列是一樣的,那麼圖片自然就插入到列裡面了
實現程式碼如下:
var data=[
{"src":"1.jpg"},
{"src":"2.jpg"},
{"src":"3.jpg"},
{"src":"4.jpg"},
{"src":"5.jpg"},
{"src":"6.jpg"},
{"src":"7.jpg"},
{"src":"8.jpg"},
{"src":"9.jpg"},
{"src":"10.jpg"},
{"src":"11.jpg"},
{"src":"12.jpg"},
{"src":"13.jpg"},
{"src":"14.jpg"},
{"src":"15.jpg"},
{"src":"16.jpg"},
{"src":"17.jpg"},
{"src":"18.jpg"},
]
$(function(){
var wrap=$('#wrap');
var boxes=wrap.children("div");
waterfall(wrap,boxes);
$(this).scroll(function(event){
appendBox(wrap,boxes)
})
})
// 主要瀑布流佈局函式
function waterfall(wrap,boxes){
// 第一步:先獲取能容納的列數:視窗寬度/每列寬度
// +20是外邊距
var boxswidth=boxes.eq(0).width()+40;
var windowwidth=$(window).width();
var column=Math.floor(windowwidth/boxswidth);
// 順便計算得出容器的寬度,讓盒子居中
var wrapwidth=column*boxswidth;
wrap.width(wrapwidth)
// 第二步:定義一個數組儲存每一列的高度
var arr=new Array;
// 遍歷每一個盒子
for(var i=0;i<boxes.length;i++){
// 當i<column時,說明在第一行,把盒子的高度存入到數組裡
if(i<column){
arr[i]=boxes.eq(i).height()+40
}
// 否則就是第二行,開始按最小高度插入圖片到列中
else{
// 先獲取最小高度列的索引
var minheight=Math.min.apply(null,arr)
var minindex=getMinIndex(minheight,arr)
// 這裡的leftvalue,是指最小高度列距離視窗左邊的距離,相當於間接定位了這一列接下來要插入圖片時,position定位的left值是多少
var leftvalue=boxes.eq(minindex).position().left;
setStyle(boxes.eq(i),minheight,leftvalue,i)
// 到這裡已經插入了一張圖片在最低高度列,接下來要改變arr裡的最低高度的值,讓它繼續下次遍歷
arr[minindex]+=boxes.eq(i).height() + 20
}
}
}
//設定追加盒子的樣式 減少重新整理量
var getStartNumber=0;
var setStyle=function(box,top,left,index){
if(getStartNumber>=index){
return false;
};
box.css({
'position':'absolute',
'top':top,
'left':left,
'opacity':'0'
}) .stop().animate({/*第二行慢慢出來的動畫*/
'opacity':'1'
},1000);
getStartNumber=index;
};
// 計算最小高度列的索引函式
function getMinIndex(minheight,arr){
var minindex=arr.indexOf(minheight)
return minindex
}
// 判斷是否在底部的函式
function getBottom(wrap){
// 獲取最後一列的高度和滾動的高度+視窗高度的和,如果長的一列的高度比視窗+滾動的高度要小,說明到底了需要追加
var documentHeight=$(window).height();
var scrollHeight=$(window).scrollTop();
var boxes = wrap.children('div')
var lastboxTop=boxes.eq(boxes.length-1).offset().top;
var lastboxHeight=boxes.eq(boxes.length-1).height()+20;
var totalHeight=lastboxHeight+lastboxTop
return documentHeight+scrollHeight>=totalHeight?true:false;
}
// 追加瀑布流效果
function appendBox(wrap,boxes){
// 先判斷是否展示到了底部
if(getBottom(wrap)){
for (i in data){
var addstr="<div class='item'> <img src='images/"+data[i].src+"' alt=''> </div>"
wrap.append(addstr)
}
}else{
return false
}
waterfall(wrap,wrap.children('div'))
}