1. 程式人生 > >關於瀑布流的佈局原理分析(純CSS瀑布流與JS瀑布流)

關於瀑布流的佈局原理分析(純CSS瀑布流與JS瀑布流)

瀑布流 又稱瀑布流式佈局,是比較流行的一種網站頁面佈局方式。即多行等寬元素排列,後面的元素依次新增到其後,等寬不等高,根據圖片原比例縮放直至寬度達到我們的要求,依次按照規則放入指定位置。

 

為什麼使用瀑布流

瀑布流佈局在我們現在的前端頁面中經常會用的到,它可以有效的降低頁面的複雜度,節省很多的空間,對於整個頁面不需要太多的操作,只需要下拉就可以瀏覽使用者需要看到的資料;並且,在當前這個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程式碼實現

實現思路:

  1.   設定每一列圖片的寬度和間距
  2.   獲取當前視窗的總寬度,從而根據圖片寬度去旁段分成幾列
  3.   獲取所有圖片元素,定義一個空陣列來儲存高度
  4.   遍歷所有容器,開始判斷  當頁面載入完成,或頁面寬度發生變化時,呼叫函式。
    • 如果當前處於第一行時: 直接設定圖片位置【 即 top為間距的大小,left為(當前圖片的寬度+間距) * 當前圖片的值+間距大小 】,並儲存當前元素高度。
    • 如果當前不處於第一行時:進行高度對比,通過遍歷迴圈,拿到最小高度和相對應的索引,設定圖片位置【 即 top為最小高度值+間距*2,left為 (當前圖片的寬度+間距) * 索引 值+間距大小)】,並修改當前索引的高度為當前元素高度。
  5.   當頁面載入完成,或頁面寬度發生變化時,呼叫函式。

 

程式碼實現

 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         
12         // 獲取滾動條的寬度
13         function getScrollbarWidth() {
14             const oDiv = document.createElement('div');//建立一個div
15             // 給div設定樣式。隨便定義寬高,只要能獲取到滾動條就可以
16             oDiv.style.cssText = `width: 50px;height: 50px;overflowY: scroll;` 
17             document.body.appendChild(oDiv);//把div新增到body中
18             const scrollbarWidth = oDiv.offsetWidth - oDiv.clientWidth;// 使最大寬度和可視寬度相減,獲得到滾動條寬度。
19             oDiv.remove();//移除建立的div
20             return scrollbarWidth;//返回滾動條寬度
21         }
22         const heightArr = []; // 定義一個空陣列,儲存最低高度。
23         for (let i = 0; i < items.length; i++) {
24             // 遍歷所有的外層容器
25             const height = items[i].offsetHeight;
26             // 如果當前處在第一行
27             if (i < column) {
28                 // 直接設定元素距離上部的位置和距離左邊的距離。
29                 items[i].style.cssText = `top: ${gap}px;left: ${(itemWidth + gap) * i + gap}px`;
30                 // 儲存當前元素的高度。
31                 heightArr.push(height);
32             } else {
33                 // 不是第一行的話,就進行比對。
34                 let minHeight = heightArr[0]; // 先儲存第一項的高度
35                 let minIndex = 0; // 儲存第一項的索引值
36                 for (let j = 0; j < heightArr.length; j++) {
37                     // 通過迴圈遍歷比對,拿到最小值和最小值的索引。
38                     if (minHeight > heightArr[j]) {
39                         minHeight = heightArr[j];
40                         minIndex = j;
41                     }
42                 }
43                 // 通過最小值為當前元素設定top值,通過索引為當前元素設定left值。
44                 items[i].style.cssText = `top: ${minHeight + gap *2}px; left: ${(itemWidth + gap) * minIndex + gap}px`;
45                 // 並修改當前索引的高度為當前元素的高度
46                 heightArr[minIndex] = minHeight + gap + height;
47             }
48         }
49     }
50     // 頁面載入完成呼叫一次。
51     window.onload = fall;
52     // 頁面尺寸發生改變再次呼叫。
53     window.onresize = fall;
54 </script>

最終效果圖:

 

總結瀑布流佈局原理

  • 設定圖片寬度一致
  • 根據瀏覽器寬度以及每列寬度計算出列表個數,列表預設0
  • 當圖片載入完成,所有圖片依次放置在最小的列數下面
  • 父容器高度取列表陣列的最大值

引申知識點

  • let,const以及var三者的區別
  • 滾動載入圖片(懶載入原理)

&n