「乾貨」深度解析瀑布流佈局


「乾貨」深度解析瀑布流佈局


前言

2020年3月中旬,最近對佈局相關的內容比較有興趣,在此整理一下和瀑布流相關的使用場景以及多種實現方案。

瀑布流佈局

瀑布流又稱瀑布流式佈局,是一種比較流行的頁面佈局方式,專業的英文名稱為[Masonry Layouts]。與傳統的分頁顯示不同,視覺表現為參差不齊的多欄佈局,最早是由Pinterest首先運用。

無圖無真相:


「乾貨」深度解析瀑布流佈局


如圖所示,網頁上呈現參差不齊的多欄佈局,圖片等寬不等高,根據圖片原比例縮放直至寬度達到固定的要求,每行排滿後,後面的元素依次添加到其後,視覺上顯得錯落有致不拘一格。

瀑布流的優點

優點如下:

  • 節省空間,外表美觀,更有藝術性。
  • 對於觸屏設備非常友好,通過向上滑動瀏覽
  • 用戶瀏覽時的觀賞和思維不容易被打斷,留存更容易。

從體驗的心理講,女性是一種逛街數小時都不需要停歇的生物,一眼望不到頭的瀑布流契合了這種心理。瀑布流的圖片就像商品,就像逛街、就像掃貨。女性只要不斷往下拉伸頁面,就像置身在一條沒有盡頭的購物街,沒有層高限制的商場中一樣。傳統佈局中的下一頁就是打斷,好比男朋友輕聲在耳邊說了句:休息一下吧,我累了……結果不言而喻~

瀑布流的缺點

缺點如下:

  • 用戶無法瞭解內容總長度,對內容沒有宏觀掌控。
  • 用戶無法瞭解現在所處的具體位置,不知道離終點還有多遠。
  • 回溯時不容易定位到之前看到的內容。
  • 容易造成頁面加載的負荷。
  • 容易造成用戶瀏覽的疲勞,沒有短暫的休息時間。

瀑布流的適用場景

根據瀑布流的優缺點,我們不難得出在什麼情況下選擇瀑布流是合理的選擇:

  • 內容以圖片為主的時候,瀑布流是更好的選擇。圖片佔用空間比較大,並且大腦理解的速度相比理解文字要快,短時間內可以掃過的內容很多,所以如果用分頁顯示的話用戶務必會頻繁的翻頁,影響沉浸式的體驗,而瀑布流可以解決這個問題。
  • 信息與信息之間相對獨立時,瀑布流是更好的選擇。如果信息關聯性強,用戶務必會進行大量的回溯操作去查看之前或者之後的信息,相反,如果信息相對獨立的話,可以使用瀑布流,讓用戶同時接受來自不同地方的信息。
  • 信息與搜索匹配比較模糊時,瀑布流是更好的選擇。瀑布流給人的直觀印象,就是同時顯示的信息與用戶搜索的匹配度大致一樣,而分頁顯示的直觀印象則是越靠上的信息被認為與用戶的搜索越匹配。因此,當信息與搜索匹配度沒有明顯區分度時,可以採用瀑布流。
  • 用戶目的性不強的時候,瀑布流是更好的選擇。如果用戶有特定需要查找的信息,分頁查找定位更方便,而當目的性較弱的時候,瀑布流可以增加用戶停留的時間和意想不到的收穫。

multi-column 多欄佈局實現瀑布流

通常Multi-column用於文本的分列:

<code>.container {
column-count: 3;
}
複製代碼/<code>


「乾貨」深度解析瀑布流佈局


multi-column佈局中子元素的排列順序是先從上往下再從左至右。


「乾貨」深度解析瀑布流佈局


根據這個特性,我們就可以用來實現瀑布流。

multi-column實現瀑布流主要依賴以下幾個屬性:

  • column-count: 設置共有幾列
  • column-width: 設置每列寬度,列數由總寬度與每列寬度計算得出
  • column-gap: 設置列與列之間的間距

column-count和column-width都可以用來定義分欄的數目,而且並沒有明確的優先級之分。優先級的計算取決於具體的場景。

計算方式為:計算column-count和column-width轉換後具體的列數,哪個小就用哪個。

一個圖片&文字的例子:

<code>



...



...



複製代碼/<code>
<code>.masonry{
column-count: 3;
column-gap: 10px;
}
.masonry .item{
border:1px solid #999;
margin-bottom: 10px;
}
.masonry .item img{

width: 100%;
}
複製代碼/<code>

點擊查看在線DEMO及完整代碼

效果如下:


「乾貨」深度解析瀑布流佈局


我們可以看到,雖然實現了瀑布流的效果,但奇怪的是例子中前兩列的最後一個元素的文本內容被自動斷開,一部分在當前列尾,一部分在下一列的列頭。

我的理解是multi-column佈局會將其內的元素自動進行流動和平衡,儘可能保證每列的高度趨於相同,所以會將其內的文本階段分佈在兩列內。

而這種展示方式無疑是我們不希望看到的,我們希望的是每個元素都是獨立的,前後不斷開,此時我們需要使用break-inside來實現。

break-inside: auto | avoid

  • auto: 元素可以中斷
  • avoid: 元素不能中斷

修改一下之前的例子:

<code>.masonry .item{
break-inside: avoid;
}
複製代碼/<code>

點擊查看在線DEMO及完整代碼

效果如下:


「乾貨」深度解析瀑布流佈局


效果實現了,但由於multi-column佈局中子元素的排列順序是先從上往下再從左至右,所以這種方式僅適用於數據固定不變的情況,對於滾動加載更多等可動態添加數據的情況就並不適用了。

關於column更多的用法,參見MDN 關於column的兼容性,參見caniuse

grid 佈局實現瀑布流

Grid佈局是最強大的 CSS 佈局方案。

它將網頁劃分成一個個網格,可以任意組合不同的網格,做出各種各樣的佈局。以前,只能通過複雜的 CSS 框架達到的效果,現在瀏覽器內置了。


「乾貨」深度解析瀑布流佈局


上圖這樣的佈局,就是 Grid 佈局的拿手好戲,因此,我們就可以用Grid來實現瀑布流。

為實現瀑布流先介紹以下幾個屬性:

  • display:設置為grid指明當前容器為Grid佈局
  • grid-template-columns: 定義每一列的列寬
  • grid-template-rows: 定義每一行的行高
  • column-gap:用於設置列間距

grid-template-columns和grid-template-rows,可以使用絕對單位,也可以使用百分比。並且為了表示比例關係,Grid佈局提供了fr關鍵字,如果設置1fr和2fr,表示後者是前者的兩倍。

根據以上幾個屬性,先寫一個例子出來,看看效果:

<code>




複製代碼/<code>
<code>.masonry{
display: grid;
grid-template-rows: 1fr 1fr 1fr; // 分為3行
grid-template-columns: 1fr 1fr 1fr; // 分為3列
column-gap:5px; // 列間距5px
}

複製代碼/<code>

點擊查看在線DEMO及完整代碼

效果如下:


「乾貨」深度解析瀑布流佈局


我們看到高度不同的div塊分佈在每一個單元格內,但還沒有實現瀑布流的效果。

為實現瀑布流再介紹幾個屬性:

  • grid-row-start:上邊框所在的水平網格線
  • grid-row-end:下邊框所在的水平網格線
  • grid-column-start:左邊框所在的垂直網格線
  • grid-column-end:右邊框所在的垂直網格線

那麼什麼是網格線呢?

劃分網格的線,稱為網格線。水平網格線劃分出行,垂直網格線劃分出列。

正常情況下,n行有n + 1根水平網格線,m列有m + 1根垂直網格線,比如三行就有四根水平網格線。


「乾貨」深度解析瀑布流佈局


上圖是一個 4 x 4 的網格,共有5根水平網格線和5根垂直網格線。

這4個屬性可接收如下屬性:

  • auto:表示自動放置
  • 自定義名稱:可以給予網格線一個名稱,並在此處引用(本文並不涉及)
  • 網格線索引: 代表第幾條網格線(從1開始)
  • span + 數字 : 表示上下邊框或左右邊框跨越多少網格

來看看這個網格線有什麼用處

為方便查看,我們讓例子中的每個div塊高度修改為100%,並將樣式代碼修改為:

<code>.item{
height:100%;
}
.item:first-child{
grid-row-start:1;
grid-row-end:span 2;
}
複製代碼/<code>

點擊查看在線DEMO及完整代碼


「乾貨」深度解析瀑布流佈局


我們對Grid佈局中的第一項添加了grid-row-start:1和grid-row-end:span 2,令其上邊框位於1水平網格線,下邊框距上邊框跨越2個水平網格線。從效果上看來,是不是有點像瀑布流了呢!

在之前的例子中,我們分別指定了grid-template-columns和grid-template-rows用於定義幾行幾列,由於行列數的確定,其內的每個單元格的寬高也被確定了,而實際的瀑布流佈局中,寬度是固定的,而高度是動態的,並且具體的行數也是無法在開始時確定的,所以我們需要在Grid佈局中不指定行高(grid-template-rows)。

介紹另一個屬性:

  • grid-auto-rows:用來設置多餘網格的行高

結合剛才說的Grid實現的瀑布流佈局中,不設置行高(grid-template-rows),此時設置grid-auto-rows後,所有單元格的高度均為grid-auto-rows指定的值。

由於grid-row-start和grid-row-end可以指定單元格的上邊距和下邊距位置,也就是說可以將單元格的高度拉伸,而原有高度由grid-auto-rows決定,我們僅需將grid-auto-rows設置一個很小的值,比如10px,然後對其進行拉伸將其高度指定為真實高度,每一個單元格都做如下操作,那麼瀑布流就實現了~

假設第一個單元格內容真實高度為100px,由於grid-auto-rows:10px,那麼我們可以這樣設置:

<code>.item1{
grid-row-start:'auto';
grid-row-end:span 10;
}
複製代碼/<code>

假設第二個單元格內容真實高度為150px,由於grid-auto-rows:10px,那麼我們可以這樣設置:

<code>.item2{
grid-row-start:'auto';
grid-row-end:span 15;
}
複製代碼/<code>

當然了,在實際情況中,瀑布流更多的是為圖片的展示而服務的,並且由於圖片是異步請求加載,只有在加載完成後才能獲取圖片的真實寬高,所以不得不使用JS來動態將單元格高度進行拉伸。

偽代碼如下:

<code>    //image-dom
let img = document.getElementsByTagName('img')[0];
//image-dom 當前寬度
let width = img.width;

let image = new Image();
image.src = 'xxxx.img';
image.onload = function(){
//圖片原寬
let w = image.width;
//圖片原高
let h = image.height;
//image-dom的真實高度(依據當前寬度及圖片真實寬高)
let height = Math.round(h * width / w)
//設置當前跨越幾個網格(每個網格10px)
img.style.gridRowEnd = `span ${~~(height/10)}`
}
複製代碼/<code>

點擊查看在線DEMO及完整代碼

效果如下:


「乾貨」深度解析瀑布流佈局


關於column更多的用法,參見MDN 關於column的兼容性,參見caniuse

Flexbox 實現瀑布流

Flexbox佈局到今天已經是使用非常廣泛的,也算是很成熟的一個特性。在此就不再介紹Flexbox佈局的相關內容,如果還有不是很瞭解的朋友,可參見阮一峰的《Flex 佈局教程:語法篇》

那接下來我們就看Flexbox怎麼實現瀑布流佈局。

此時,我們需要將html結構設計成如下結構:

<code>

















複製代碼/<code>

上面代碼中div.masonry代表當前瀑布流容器,div.column代表每一列的容器,div.item代表每一列中的每一項。

我們需要將div.masonry和div.column都通過display:flex將其設置為Flex容器。

不同的是瀑布流容器主軸方向設置為水平方向flex-direction:row,列容器主軸方向設置為垂直方向flex-direction:column

<code>.masonry {
display: flex; // 設置為Flex容器
flex-direction: row; // 主軸方向設置為水平方向
}

.column {
display: flex; // 設置為Flex容器
flex-direction: column; // 主軸方向設置為垂直方向
}
複製代碼/<code>

由於當前的html結構分為了瀑布流容器和列容器,並且常見的需求圖片均是從左至右再從上到下來進行排列,所以需要通過Javascript來區分每一列的具體數據:

假設分為三列,偽代碼如下:

<code>let data1 = [], //第一列
data2 = [], //第二列
data3 = [], //第三列
i = 0;
while (i < data.length) {
data1.push(data[i++]);
if (i < data.length) {
data2.push(data[i++]);
}
if (i < data.length) {
data3.push(data[i++]);
}
}
return {
//第一列
data1,
//第二列
data2,
//第三列
data3
};
複製代碼/<code>

點擊查看在線DEMO及完整代碼

效果如下:


「乾貨」深度解析瀑布流佈局


總結

做瀑布流需要考慮幾方面大因素,圖片質量,圖片大小,加載速度,如果這些不能同時滿足,會大大降低用戶體驗。個人覺得瀑布流對於觸屏終端體驗會更好一些。

本文總結了multi-column、grid、Flexbox三種方式實現瀑布流,實現方案各有不同,從兼容性及易用性綜合考慮,還是推薦使用Flexbox的佈局實現方案。

本文介紹的瀑布流佈局方案,本質上可稱為豎向瀑布流,篇幅有限,並未涉及橫向瀑布流的內容,關於橫向瀑布流的內容,會在接下來的文章中繼續總結,敬請期待。

參考

  • 瀑布流的設計體驗真的很好嗎?
  • 寫給自己看的CSS columns分欄佈局教程
  • Grid佈局20行代碼快速生成瀑布流
  • Grid 佈局教程
  • Flex 佈局教程:語法篇

寫在最後

  • 文中如有錯誤,歡迎在評論區指正,如果這篇文章幫到了你,歡迎點贊和關注
  • 可在github中找到更多精品文章,歡迎Watch & Star ★
  • github地址:https://github.com/chenqf/frontEndBlog


分享到:


相關文章: