關於瀑布流的佈局原理分析(純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程式碼實現
實現思路:
- 設定每一列圖片的寬度和間距
- 獲取當前視窗的總寬度,從而根據圖片寬度去旁段分成幾列
- 獲取所有圖片元素,定義一個空陣列來儲存高度
- 遍歷所有容器,開始判斷 當頁面載入完成,或頁面寬度發生變化時,呼叫函式。
- 如果當前處於第一行時: 直接設定圖片位置【 即 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 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