css3 實現(xiàn)瀑布流布局

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

瀑布流布局其核心是基于一個網(wǎng)格的布局,而且每行包含的項目列表高度是隨機的(隨著自己內(nèi)容動態(tài)變化高度),同時每個項目列表呈堆棧形式排列,最為關(guān)鍵的是,堆棧之間彼此之間沒有多余的間距差存大。還是上張圖來看看我們說的瀑布流布局是什么樣子。

image

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

image

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

Multi-columns

首先最早嘗試使用純CSS方法解決瀑布流布局的是CSS3 的Multi-columns。其最早只是用來用來實現(xiàn)文本多列排列(類似報紙雜志樣的文本排列)。但對于前端同學(xué)來說,他們都是非常具有創(chuàng)意和創(chuàng)新的,有人嘗試通過Multi-columns相關(guān)的屬性column-countcolumn-gap配合break-inside來實現(xiàn)瀑布流布局
比如我們有一個類似這樣的HTML結(jié)構(gòu):

<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。為了節(jié)約篇幅,上面代碼僅列了兩個。結(jié)構(gòu)有了,現(xiàn)在來看CSS。在.masonry中設(shè)置column-countcolumn-gap,前者用來設(shè)置列數(shù),后者設(shè)置列間距:

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

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

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

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

.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. -->

比如下面的這個示例:點擊預(yù)覽

示例1.png

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

Flexbox


Flexbox布局到今天已經(jīng)是使用非常廣泛的,也算是很成熟的一個特性。那接下來我們就看Flexbox怎么實現(xiàn)瀑布流布局。如果你從未接觸過Flexbox相關(guān)的屬性,那建議你點擊這里閱讀。如果你覺得這里信息量過于太多,那強列建議你閱讀下面幾篇文章,閱讀完之后你對Flexbox相關(guān)屬性會有一個徹底的了解:

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

一個主要的列容器


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

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

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

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

.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;
    }
}

同樣來看一個示例效果:點擊預(yù)覽

示例2.png

這個解決方案有一個最致命的地方,就是需要顯式的給.masonry設(shè)置height,特別對于響應(yīng)式設(shè)計來說這個更為不友好。而且當我們的項目列表是動態(tài)生成,而且內(nèi)容不好控制之時,這就更蛋疼了。那么有沒有更為友好的方案呢?

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

單獨的列容器

這個方案,我們需要對我們的HTML結(jié)構(gòu)做一個變更。變更后的HTML結(jié)構(gòu)看起來像這樣:

<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>

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

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

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

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

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

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

這種方案對應(yīng)的響應(yīng)式設(shè)計,需要在不同的媒體查詢下修改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);
    }
}

效果如下:點擊預(yù)覽

示例3.png

從實戰(zhàn)結(jié)果已經(jīng)告訴你答案了。只不過在結(jié)構(gòu)上變得冗余一點。

Grid

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

Grid制作瀑布流,對于結(jié)構(gòu)而言和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-rowgrid-column來指定列表項目所在的區(qū)域,比如:

.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過來:點擊預(yù)覽

WX20181229-103956@2x.png

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

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

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

總結(jié)

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

著作權(quán)歸作者所有。
商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。
原文: https://www.w3cplus.com/css/pure-css-create-masonry-layout.html ? w3cplus.com

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內(nèi)容