「前端進階」深度解析瀑布流佈局

你知道的越多,你不知道的越多
點贊再看,手留餘香,與有榮焉javascript

前言

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

瀑布流佈局

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

無圖無真相:前端

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

瀑布流的優勢

優勢以下:css3

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

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

瀑布流的缺點

缺點以下:github

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

瀑布流的適用場景

根據瀑布流的優缺點,咱們不可貴出在什麼狀況下選擇瀑布流是合理的選擇:瀏覽器

  • 內容以圖片爲主的時候,瀑布流是更好的選擇。圖片佔用空間比較大,而且大腦理解的速度相比理解文字要快,短期內能夠掃過的內容不少,因此若是用分頁顯示的話用戶務必會頻繁的翻頁,影響沉浸式的體驗,而瀑布流能夠解決這個問題。微信

  • 信息與信息之間相對獨立時,瀑布流是更好的選擇。若是信息關聯性強,用戶務必會進行大量的回溯操做去查看以前或者以後的信息,相反,若是信息相對獨立的話,可使用瀑布流,讓用戶同時接受來自不一樣地方的信息。

  • 信息與搜索匹配比較模糊時,瀑布流是更好的選擇。瀑布流給人的直觀印象,就是同時顯示的信息與用戶搜索的匹配度大體同樣,而分頁顯示的直觀印象則是越靠上的信息被認爲與用戶的搜索越匹配。所以,當信息與搜索匹配度沒有明顯區分度時,能夠採用瀑布流。

  • 用戶目的性不強的時候,瀑布流是更好的選擇。若是用戶有特定須要查找的信息,分頁查找定位更方便,而當目的性較弱的時候,瀑布流能夠增長用戶停留的時間和意想不到的收穫。

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

一般Multi-column用於文本的分列:

.container {
  column-count: 3;
}
複製代碼

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

根據這個特性,咱們就能夠用來實現瀑布流

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

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

column-countcolumn-width均可以用來定義分欄的數目,並且並無明確的優先級之分。優先級的計算取決與具體的場景。

計算方式爲:計算column-countcolumn-width轉換後具體的列數,哪一個小就用哪一個。

一個圖片&文字的例子:

<div class="masonry">
    <div class="item">
        <img src="..."/>
        <span class="title">...</span>
    </div>
    <div class="item">
        <img src="..."/>
        <span class="title">...</span>
    </div>
    <!-- more items-->
</div>
複製代碼
.masonry{
    column-count: 3;
    column-gap: 10px;
}
.masonry .item{
    border:1px solid #999;
    margin-bottom: 10px;
}
.masonry .item img{
    width: 100%;
}
複製代碼

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

效果以下:

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

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

而這種展現方式無疑是咱們不但願看到的,咱們但願的是每一個元素都是獨立的,先後不斷開,此時咱們須要使用break-inside來實現。

break-inside: auto | avoid

  • auto: 元素能夠中斷
  • avoid: 元素不能中斷

修改一下以前的例子:

.masonry .item{
    break-inside: avoid;
}
複製代碼

點擊查看在線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-columnsgrid-template-rows,可使用絕對單位,也可使用百分比。而且爲了表示比例關係,Grid佈局提供了fr關鍵字,若是設置1fr2fr,表示後者是前者的兩倍。

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

<div class="masonry">
    <div class="item"></div>
    <!-- more items-->
</div>
複製代碼
.masonry{
    display: grid;
    grid-template-rows: 1fr 1fr 1fr; // 分爲3行
    grid-template-columns: 1fr 1fr 1fr; // 分爲3列
    column-gap:5px; // 列間距5px
}

複製代碼

點擊查看在線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%,並將樣式代碼修改成:

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

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

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

在以前的例子中,咱們分別指定了grid-template-columnsgrid-template-rows用於定義幾行幾列,因爲行列數的肯定,其內的每一個單元格的寬高也被肯定了,而實際的瀑布流佈局中,寬度是固定的,而高度是動態的,而且具體的行數也是沒法在開始時肯定的,因此咱們須要在Grid佈局中不指定行高(grid-template-rows)。

介紹另外一個屬性:

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

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

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

假設第一個單元格內容真實高度爲100px,因爲grid-auto-rows:10px,那麼咱們能夠這樣設置:

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

假設第二個單元格內容真實高度爲150px,因爲grid-auto-rows:10px,那麼咱們能夠這樣設置:

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

固然了,在實際狀況中,瀑布流更多的是爲圖片的展現而服務的,而且因爲圖片是異步請求加載,只有在加載完成後才能獲取圖片的真實寬高,因此不得不使用JS來動態將單元格高度進行拉伸。

僞代碼以下:

//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)}`
    }
複製代碼

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

效果以下:

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

Flexbox 實現瀑布流

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

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

此時,咱們須要將html結構設計成以下結構:

<div class="masonry">
    <!-- 第一列 -->
    <div class="column">
        <div class="item"></div>
        <!-- more items-->
    </div>
    <!-- 第二列 -->
    <div class="column">
        <div class="item"></div>
        <!-- more items-->
    </div>
    <!-- 第三列 -->
    <div class="column">
        <div class="item"></div>
        <!-- more items-->
    </div>
</div>
複製代碼

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

咱們須要將div.masonrydiv.column都經過display:flex將其設置爲Flex容器。

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

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

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

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

效果以下:

總結

作瀑布流須要考慮幾方面大因素,圖片質量,圖片大小,加載速度,若是這些不能同時知足,會大大下降用戶體驗。我的以爲瀑布流對於觸屏終端體驗會更好一些。

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

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

參考

寫在最後

  • 文中若有錯誤,歡迎在評論區指正,若是這篇文章幫到了你,歡迎點贊關注
  • 本文同步首發與github,可在github中找到更多精品文章,歡迎Watch & Star ★
  • 後續文章參見:計劃

歡迎關注微信公衆號【前端小黑屋】,每週1-3篇精品優質文章推送,助你走上進階之旅

同時歡迎加我好友,回覆加羣,拉你入羣,和我一塊兒學前端~

相關文章
相關標籤/搜索