純CSS實現瀑布流佈局

瀑布流佈局有一個專業的英文名稱Masonry Layouts。瀑布流佈局已經有好多年的歷史了,我最早知道這個名詞的時候大約是在2012年,當時Pinterest網站的佈局就是使用的這種流式佈局,簡言之像Pinterest網站這樣的佈局就稱之爲瀑布流佈局,也有人稱之爲Pinterest 佈局。

瀑布流佈局其核心是基於一個網格的佈局,而且每行包含的項目列表高度是隨機的(隨着自己內容動態變化高度),同時每個項目列表呈堆棧形式排列,最爲關鍵的是,堆棧之間彼此之間沒有多餘的間距差存大。還是上張圖來看看我們說的瀑布流佈局是什麼樣子。

這裏寫圖片描述

當初要實現這樣的佈局都是依賴於JavaScript來實現,所以當時出現過很多實現瀑布流佈局的插件。比如Masonry、Isotope等都是非常有名的插件。但使用純CSS來實現,當時還是非常困難的,不管是使用float還是inline-block佈局都無法很好的控制列表項目堆棧之間的間距。最終得到的效果就像下面這樣:著作權歸作者所有。

這裏寫圖片描述

現在距離2012年已經過去了五個年頭,CSS的技術更新也是日新月異,在這幾年當中出現了很多新的佈局方法,比如多列布局multi-columns、Flexbox佈局以及今年瀏覽器支持有Grid佈局。早前在《CSS佈局的未來》一文中有對這些佈局做過闡述。既然CSS的佈局有這麼多的變化,那麼今天有沒有不借助任何JavaScript(純CSS方案)能否實現瀑布流佈局?答案是肯定的,接下來的內容,我們就使用不同的CSS佈局方案來實現瀑布流佈局。


Multi-columns

首先最早嘗試使用純CSS方法解決瀑布流佈局的是CSS3 的Multi-columns。其最早只是用來用來實現文本多列排列(類似報紙雜誌樣的文本排列)。但對於前端同學來說,他們都是非常具有創意和創新的,有人嘗試通過Multi-columns相關的屬性column-count、column-gap配合break-inside來實現瀑布流佈局。

比如我們有一個類似這樣的HTML結構:

<div class="masonry"> 
    <div class="item"> 
        <div class="item__content"> </div> 
    </div> 
    <div class="item"> 
        <div class="item__content"> </div>  
    </div> 
    <!-- more items --> 
</div>

其中div.masonry是瀑布流的容器,其裏面放置了n個列表div.item。爲了節約篇幅,上面代碼僅列了兩個。結構有了,現在來看CSS。在.masonry中設置column-count和column-gap,前者用來設置列數,後者設置列間距:

.masonry { column-count: 5; column-gap: 0; }

上面控制了列與列之間的效果,但這並不是最關鍵之處。當初純CSS實現瀑布流佈局中最關鍵的是堆棧之間的間距,而並非列與列之間的控制(說句實話,列與列之間的控制float之類的就能很好的實現)。找到實現痛楚,那就好辦了。或許你會問有什麼CSS方法可以解決這個。在CSS中有一個break-inside屬性,這個屬性也是實現瀑布流佈局最關鍵的屬性。

.item { break-inside: avoid; box-sizing: border-box; padding: 10px; }

其中break-inside:avoid爲了控制文本塊分解成單獨的列,以免項目列表的內容跨列,破壞整體的佈局。當然爲了佈局具有響應式效果,可以藉助媒體查詢屬性,在不同的條件下使用column-count設置不同的列,比如:

.masonry { 
    column-count: 1; // one column on mobile 
} 
@media (min-width: 400px) { 
    .masonry { 
        column-count: 2; // two columns on larger phones 
    } 
} 
@media (min-width: 1200px) { 
    .masonry { 
        column-count: 3; // three columns on...you get it 
    } 
} 
<!-- etc. -->

比如下面的這個示例:

http://codepen.io/airen/pen/ybyvEM

這裏寫圖片描述

看到上面示例的效果,這個時候是不是有點成就感了,是不是覺得CSS更神奇了?


Flexbox

Flexbox佈局到今天已經是使用非常廣泛的,也算是很成熟的一個特性。那接下來我們就看Flexbox怎麼實現瀑布流佈局。如果你從未接觸過Flexbox相關的屬性,那建議你點擊這裏閱讀。如果你覺得這裏信息量過於太多,那強列建議你閱讀下面幾篇文章,閱讀完之後你對Flexbox相關屬性會有一個徹底的瞭解:

一個完整的Flexbox指南
圖解CSS3 Flexbox屬性
理解Flexbox:你需要知道的一切
終極Flexbox屬性查詢列表
Flexbox佈局實戰
深入理解 flex 佈局以及計算

上面這幾篇文章告訴了你有關於Flexbox的一切: 
這裏寫圖片描述

接下來回到我們今天的正題當中,使用Flexbox實現瀑布流佈局有兩種方案。


一個主要的列容器

結構依舊和Multi-columns小節中展示的一樣。只是在.masonry容器中使用的CSS不一樣:

.masonry {
    display: flex;
    flex-flow: column wrap;
    width: 100%;
    height: 800px;
}

之前在.masonry中是通過column-count來控制列,這裏採用flex-flow來控制列,並且允許它換行。這裏關鍵是容器的高度,示例中顯式的設置了height屬性,當然除了設置px值,還可以設置100vh,讓.masonry容器的高度和瀏覽器視窗高度一樣。記住,這裏height可以設置成任何高度值(採用任何的單位),但不能不顯式的設置,如果沒有顯式的設置,容器就無法包裹住項目列表。

使用Flexbox佈局,對於.item可以不再使用break-inside:avoid,但其它屬性可以是一樣。同樣的,響應式設置,使用Flexbox實現響應式佈局比多列布局要來得容易,他天生就具備這方面的能力,只不過我們這裏需要對容器的高度做相關的處理。前面也提到過了,如果不給.masonry容器顯式設置高度是無法包裹項目列表的,那麼這裏響應式設計中就需要在不同的媒體查詢條件下設置不同的高度值:

.masonry { 
    height: auto; 
} 
@media screen and (min-width: 400px) { 
    .masonry { height: 1600px; } 
} 
@media screen and (min-width: 600px) { 
    .masonry { height: 1300px; } 
} 
@media screen and (min-width: 800px) { 
    .masonry { height: 1100px; } 
} 
@media screen and (min-width: 1100px) { 
    .masonry { height: 800px; } 
}

同樣來看一個示例效果: 
http://codepen.io/airen/pen/jmEzEm

這裏寫圖片描述

這個解決方案有一個最致命的地方,就是需要顯式的給.masonry設置height,特別對於響應式設計來說這個更爲不友好。而且當我們的項目列表是動態生成,而且內容不好控制之時,這就更蛋疼了。那麼有沒有更爲友好的方案呢?

前面說到過Flexbox有兩種方案,那咱們先來看方案二,再來回答這個問題。

單獨的列容器

這個方案,我們需要對我們的HTML結構做一個變更。變更後的HTML結構看起來像這樣:

<div class="masonry">
    <div class="column">
        <div class="item">
        <div class="item__content">
        </div>
        </div>
        <!-- more items -->
    </div>
    <div class="column">
        <div class="item">
        <div class="item__content">
        </div>
        </div>
        <!-- more items -->
    </div>
    <div class="column">
        <div class="item">
        <div class="item__content">
        </div>
        </div>
        <!-- more items -->
    </div>
</div>

不難發現,在div.item外面包了一層div.column,這個div.column稱爲列表項目的單獨容器。在這個解決方案中,.masonry和.column都通過display:flex屬性將其設置爲Flex容器,不同的是.masonry設置爲行(flex-direction:row),而.column設置爲列(flex-direction):

.masonry {
    display: flex;
    flex-direction: row;
}

.column {
    display: flex;
    flex-direction: column;
    width: calc(100%/3);
}

這裏有一個需要注意,在.column咱們通過calc()方法來控制每個列的寬度,如果你希望是三列,那麼可以設置width: calc(100% / 3);實際中根據自己的設計來設置width:

.masonry {
    display: flex;
    flex-direction: row;
}

.column {
    display: flex;
    flex-direction: column;
    width: calc(100%/3);
}

這種方案對應的響應式設計,需要在不同的媒體查詢下修改width值,比如:

.masonry {
    display: flex;
    flex-direction: column;
}

@media only screen and (min-width: 500px) {
    .masonry {
        flex-direction: row;
    }
}

.column {
    display: flex;
    flex-flow: column wrap;
    width: 100%;
}

@media only screen and (min-width: 500px) {
    .column {
        width: calc(100%/5);
    }
}

效果如下: 
http://codepen.io/ramenhog/pen/PpEPWG 
這裏寫圖片描述

從實戰結果已經告訴你答案了。只不過在結構上變得冗餘一點。


Grid

Grid將是佈局當中的一把利劍,也可以說是神器,特別是今年得到了衆多瀏覽器的支持。記得去年在CSSConf分享後,有同學問我Grid是否能實現瀑布流的佈局。說實話,雖然Grid對於佈局而言是非常的強大,但要很好的實現瀑布流佈局還是非常的蛋疼。@Rachel Andrew在她分享的文章中也特意提到過實現瀑布流的方案。從文章中摘出有關於瀑布流佈局的那部分內容。

Grid製作瀑布流,對於結構而言和Multi-columns示例中的一樣。只不過在.masonry使用display:grid來進行聲明:

.masonry {
    display: grid;
    grid-gap: 40px;
    grid-template-columns: repeat(3, 1fr);
    grid-auto-rows: minmax(50px, auto);
}

對於.item較爲蛋疼,需要分別通過grid-row和grid-column來指定列表項目所在的區域,比如:

.masonry > div:nth-child(1) {
    grid-row: 1 / 4;
    grid-column: 1;
}

.masonry > div:nth-child(2) {
    grid-row: 1 / 3;
    grid-column: 2;
}
...

將效果Fork過來: 
http://codepen.io/airen/pen/XRJEqO

這裏寫圖片描述

在Grid中有自動排列的算法的屬性:

1.如果沒有明確指定網格項目位置,網格會按自動排列算法,將它最大化利用可用空間
2.如果在當前行沒有可用位置,網格會自動搜索下一行,這樣會造成一定的差距,浪費可用空間
3.可以把grid-auto-flow的row值改變auto,可以切換搜索順序
4.grid-auto-flow還可以接受另一個關鍵詞。默認情況下,其值是sparse,但我們可以將其顯式的設置爲dense,讓網格項目試圖自動填補所有可用的空白空間

言外之意,Grid中自動排列的算法對於實現瀑布流佈局有很大的幫助。不過對於堆棧(列表項目)高度不能友好的控制。


總結

這篇文章主要介紹瞭如何使用純CSS實現瀑布流的佈局。文章簡單介紹了三種實現方案:Multi-columns、Flexbox和Grid。從上面的示例或者實現手段而言,較我友好的是Flexbox的方案。當然,隨着CSS Grid特性的完善,使用Grid實現瀑布流佈局將會變得更爲簡單和友好。那讓我們拭目以待。當然如果你覺得這些方案都不太好,你可以依舊可以考慮JavaScript的解決方案。如果你有更好的解決方案,也希望能在下面的評論中與我們一起分享。

原文: http://www.w3cplus.com/css/pure-css-create-masonry-layout.html © w3cplus.com

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章