瀑布流 又稱瀑布流式佈局,是比較流行的一種網站頁面佈局方式。即多行等寬元素排列,後面的元素依次添加到其後,等寬不等高,根據圖片原比例縮放直至寬度達到我們的要求,依次按照規則放入指定位置。 為什麼使用瀑布流 瀑布流佈局在我們現在的前端頁面中經常會用的到,它可以有效的降低頁面的複雜度,節省很多的空間,對 ...
瀑布流 又稱瀑布流式佈局,是比較流行的一種網站頁面佈局方式。即多行等寬元素排列,後面的元素依次添加到其後,等寬不等高,根據圖片原比例縮放直至寬度達到我們的要求,依次按照規則放入指定位置。
為什麼使用瀑布流
瀑布流佈局在我們現在的前端頁面中經常會用的到,它可以有效的降低頁面的複雜度,節省很多的空間,對於整個頁面不需要太多的操作,只需要下拉就可以瀏覽用戶需要看到的數據;並且,在當前這個APP至上的時代,瀑布流可以提供很好的用戶體驗,通過結合下拉刷新,上拉載入進行數據的懶載入等操作,對於用戶的體驗感來說是接近於滿分的!
瀑布流的特點
其實瀑布流的特點就是參差不齊的排列方式,以及流式佈局的擴展性,可以通過界面展示給用戶多條數據,並且讓用戶可以有向下瀏覽的衝動。
瀑布流的代碼實現
1.純 css 瀑布流:( multi-columns 方法 )
// 這裡是第一次接觸到 column-columns 這個屬性,這是一個可以設置將div元素中的文本分成幾列
//預設值是:auto
//寫法:
column-count:3;
-moz-column-count:3; /* Firefox */
-webkit-column-count:3; /* Safari and Chrome */
/* 註意:IE9及更早 IE 版本瀏覽器不支持 column-count 屬性 */
//這裡還會用到另一個屬性 column-gap,用來調整邊距,實現瀑布流佈局
html結構代碼如下:
1 <div class="demo-1">
2 <div class="item">
3 <div class="item_content content-lar" style="height:100px;" > 1</div>
4 </div>
5 <div class="item">
6 <div class="item_content content-sma"style="height:150px;" > 2</div>
7 </div>
8 <div class="item">
9 <div class="item_content content-mid"style="height:50px;" > 3</div>
10 </div>
11 <div class="item">
12 <div class="item_content content-sma" style="height:200px;" > 4</div>
13 </div>
14 <div class="item">
15 <div class="item_content content-mid"style="height:60px;" > 5 </div>
16 </div>
17 <div class="item">
18 <div class="item_content content-lar"style="height:90px;" > 6</div>
19 </div>
20 <div class="item">
21 <div class="item_content content-sma"> 7</div>
22 </div>
23 <div class="item">
24 <div class="item_content content-lar"style="height:120px;" > 8</div>
25 </div>
26 <div class="item">
27 <div class="item_content content-lar"> 9</div>
28 </div>
29 <div class="item">
30 <div class="item_content content-sma" style="height:100px;" > 10 </div>
31 </div>
32 <div class="item">
33 <div class="item_content content-mid"> 11 </div>
34 </div>
35 <div class="item">
36 <div class="item_content content-mid"style="height:100px;" > 12</div>
37 </div>
38 <!-- more items -->
39 </div>
CSS代碼如下:
1 .demo-1{
2 -moz-column-count:3; /* Firefox */
3 -webkit-column-count:3; /* Safari 和 Chrome */
4 column-count:3;
5 -moz-column-gap: 1em;
6 -webkit-column-gap: 1em;
7 column-gap: 1em;
8 width: 80%;
9 margin:0 auto;
10 }
11 .item {
12 padding: 2em;
13 margin-bottom: 2em;
14 -webkit-column-break-inside: avoid;
15 break-inside: avoid; /*防止斷點*/
16 background: #ccc;
17 text-align: center;
18 }
效果圖:
這裡有個弊端,這並不符合瀑布流的原理,如果使用純css寫瀑布流,則每一塊都是從上往下排列,不能做到從左到右排列,並且不會識別哪一塊圖片放在哪個地方合適,若是再配合動態載入,效果會特別不好,所以只能通過JS來實現瀑布流。
那麼這裡用圖片來分析一下我們想要的瀑布流是什麼樣的。
瀑布流的位置分析圖解
如下方圖片。假設一排放5張圖片。當第一排排滿足夠多的等寬圖片時,顯示的是這樣的。那麼假如我們要放第6張圖片的時候,應該放在什麼位置呢?
如果按照我們的正常邏輯來想,應該是放在第一張圖片下麵,依次水平排列過去(如下圖)
但現實並非如此!在瀑布流中,從第2行開始,接下去的每一張圖片都會放在上行中高度最低的那一列圖片下方。(如下圖)
為什麼呢?因為放置它之前,這一列的高度為所有列中最小,所以會放置在這個地方。
那麼如果再繼續放置下去,第七張圖片應該放在第三列圖片下方,以此類推。
所以每次載入圖片時,會需要判斷哪一列的圖片累計的高度最小,那麼下一張圖片就放在哪一列,即瀑布流演算法去判斷圖片的確定位置。
JS代碼實現
實現思路:
- 設定每一列圖片的寬度和間距
- 獲取當前視窗的總寬度,從而根據圖片寬度去旁段分成幾列
- 獲取所有圖片元素,定義一個空數組來保存高度
- 遍歷所有容器,開始判斷 當頁面載入完成,或頁面寬度發生變化時,調用函數。
- 如果當前處於第一行時: 直接設置圖片位置【 即 top為間距的大小,left為(當前圖片的寬度+間距) * 當前圖片的值+間距大小 】,並保存當前元素高度。
- 如果當前不處於第一行時:進行高度對比,通過遍歷迴圈,拿到最小高度和相對應的索引,設置圖片位置【 即 top為最小高度值+間距*2,left為 (當前圖片的寬度+間距) * 索引 值+間距大小)】,並修改當前索引的高度為當前元素高度。
- 當頁面載入完成,或頁面寬度發生變化時,調用函數。
代碼實現
1 <script type="text/javascript"> 2 // 定義瀑布流演算法函數 3 function fall() { 4 const minGap = 20; // 最小間距,讓每一列的最小空隙可以自定義,避免太過擁擠的情況發生。但是,會通過計算得到真實的間距。 5 const itemWidth = 300; // 每一項的寬度,即當前每一個圖片容器的寬度。保證每一列都是等寬不等高的。 6 const scrollBarWidth = getScrollbarWidth(); // 獲取滾動條的寬度 7 const pageWidth = window.innerWidth - scrollBarWidth; // 獲取當前頁面的寬度 = window.innerWidth - 滾動條的寬度 8 const column = Math.floor(pageWidth / (itemWidth + minGap)); // 實際列數=頁面寬度/(圖片寬度+最小間距) 9 const gap = (pageWidth - itemWidth * column) / column/2; // 計算真實間距 = (頁面寬度- 圖片寬度*實際列數)/實際列數/2 10 const items = document.querySelectorAll('img'); // 獲取所有的外層元素 11 const heightArr = []; // 定義一個空數組,保存最低高度。 12 13 // 獲取滾動條的寬度 14 function getScrollbarWidth() { 15 const oDiv = document.createElement('div');//創建一個div 16 // 給div設置樣式。隨便定義寬高,只要能獲取到滾動條就可以 17 oDiv.style.cssText = `width: 50px;height: 50px;overflowY: scroll;` 18 document.body.appendChild(oDiv);//把div添加到body中 19 const scrollbarWidth = oDiv.offsetWidth - oDiv.clientWidth;// 使最大寬度和可視寬度相減,獲得到滾動條寬度。 20 oDiv.remove();//移除創建的div 21 return scrollbarWidth;//返回滾動條寬度 22 } 23 24 25 for (let i = 0; i < items.length; i++) { 26 // 遍歷所有的外層容器 27 const height = items[i].offsetHeight; 28 // 如果當前處在第一行 29 if (i < column) { 30 // 直接設置元素距離上部的位置和距離左邊的距離。 31 items[i].style.cssText = `top: ${gap}px;left: ${(itemWidth + gap) * i + gap}px`; 32 // 保存當前元素的高度。 33 heightArr.push(height); 34 } else { 35 // 不是第一行的話,就進行比對。 36 let minHeight = heightArr[0]; // 先保存第一項的高度 37 let minIndex = 0; // 保存第一項的索引值 38 for (let j = 0; j < heightArr.length; j++) { 39 // 通過迴圈遍歷比對,拿到最小值和最小值的索引。 40 if (minHeight > heightArr[j]) { 41 minHeight = heightArr[j]; 42 minIndex = j; 43 } 44 } 45 // 通過最小值為當前元素設置top值,通過索引為當前元素設置left值。 46 items[i].style.cssText = `top: ${minHeight + gap *2}px; left: ${(itemWidth + gap) * minIndex + gap}px`; 47 // 並修改當前索引的高度為當前元素的高度 48 heightArr[minIndex] = minHeight + gap + height; 49 } 50 } 51 } 52 // 頁面載入完成調用一次。 53 window.onload = fall; 54 // 頁面尺寸發生改變再次調用。 55 window.onresize = fall; 56 </script>
最終效果圖:
總結瀑布流佈局原理
- 設置圖片寬度一致
- 根據瀏覽器寬度以及每列寬度計算出列表個數,列表預設0
- 當圖片載入完成,所有圖片依次放置在最小的列數下麵
- 父容器高度取列表數組的最大值
引申知識點
- let,const以及var三者的區別
- 滾動載入圖片(懶載入原理)
- 反撇號(`)基礎知識
- style和style.cssTest 的區別