[CSS] 瀑布流難題

寫在最前面,多數情況下,網站的內容使用瀑布流的呈現方式並不多。而這種呈現方式,我記得好像是因為 Pinterest 而紅起來的(有錯誤請指正)。

為何說是難題?我只是想用這個來解釋 Flexbox, Grid 遇到的困難點。


瀑布流流去哪?

給我一個 Box

這個埂知道就知道,不知道的不要問。之所以叫做 瀑布 故名思義,就是由上到下(由左到右)的一種排列方式。主要是用於呈現 卡牌類型 的內容,每一張卡牌的寬度是相同的,然後高度不同,所以由上到下依序排列出 多欄 式的結構。

另一個比較廣為人知的名字,叫做 Masonry Layout,嗯,因為當年 Masonry 團隊,製作了一個 JQuery 的外掛,用的排版佈局就是瀑布流的格式,然後超紅。所以, Masonry Layout 就有點變成了瀑布流的代名詞。

比較熱門的就這幾家,

所以,瀑布其實也可以由左到右啦,螢幕轉個 90 度就好了。


網格系統的難題

首先,網格系統有個先天上的限制,這一點無論在 Flexbox 或 Grid 都成立。那就是每個區塊的尺寸會像是表格一樣,無法做到尺寸任意調整這件事情。也就是說,在網格系統下無法辦到瀑布流那種 破碎區塊 的編排。我之前在聊 Grid 的時候 有提過,借個圖來用。

Grid

這樣的編排方式,即便是 Flexbox 的 flex-flow: row wrapflex-flow: column wrap 編排,其結果都會是類似的。所以,如果要打破這種先天限制,就只能藉助 JavaScript 來操作。

我們來說說為什麼是難題。首先,其實 flex-wrap: wrap 在字面上看起來很好理解,所以,你覺得你真的理解他 wrap 了什麼東西嗎?

  1. flex-flow: row wrap 以列為準的排序方式,他的 wrap 斷點在哪?
  2. flex-flow: column wrap 以行為準的排序方式,他的 wrap 斷點在哪?

歐,台灣特別的用法,不然 column 好像是叫做欄,直式,而 row 叫做列,橫式。為了避免這種地區用語的不同,後續我都會用 rowcolumn 來稱呼,比較不容易搞混。

剛剛的第一個問題,一般狀況下很容易理解,因為我們使用的裝置,都有一個 自然的最大寬度 。所以,以常用的瀏覽器而言,最大寬度不過就是你的螢幕尺寸而已,所以,對於 row 的斷點來說,就會是在 超出這個最大寬度 的地方,超出的區塊就會被切斷然後往下排去。

好了,那第二個問題呢?

當我們以 column 來排列的時候,

因為內容長度是不斷往下長,所以,他永遠不會被 wrap

所以,當我們使用 column 又想要讓他可以被中斷,那麼就需要給他一個 受限制的最大高度 ,換句話說,你只要給他 max-height: 1000px ,這個時候,道理跟 row 相同,在超出 最大高度 的地方,超出去的就會被切斷往後排下去。

所以,如果要做一個類似 Pinterest 的結構並不困難,只要用點 @media 加上 row 搭配 column 就可以達成。但,如果要做到 破碎區塊 編排,基本上單純靠 CSS 還是無法做到。


強制中離

在 Flexbox 系統中,我們可以利用 flex-basis: 100% 的作法,來達到強迫中斷某個排列的目的。我們以 row 來看,所謂的強迫中斷,意思就是安插一個 寬度 100% 的物件在那邊,所以他會被切斷。至於缺點?你會看到區塊下方會產生 留白 ,這也是為何無法做到瀑布流的一個問題。

至於使用 column 的方式,基本上 flex-basis: 100% 要有效,必須要有 絕對高度 的設定,也就是說,你必須要指定一個 height: 1000px 這樣的作法,他才會幫你中斷。至於缺點,你必須要設定 絕對高度 就是一個缺點。

大家都這麼聰明,應該會想到,是不是還有別招?有的,在 CSS Fragmentation Module Level 3 當中有列出來,但是目前還是超級草稿階段,另一個則是 CSS 2.1 就有的 page-break-*

通常會看到 page-break-* 會直覺想到跟 列印 有關,不過對於 column 的排列方式來說,他確實也可以有效中斷。但是,以目前的狀況來說扣除 CSS Multi-column Layout Module Level 1 的情況,基本上能正確運作的好像 只有 Firefox 而已 。聽說 IE 也正常,但我沒測試,有興趣的人可以試試看,但新版本 Edge 應該不行,畢竟他是 Chrome 核心。

對喔,Chrome/Safari/Opera 基本上都 GG 思密達~

Firefox 天生神力

當你使用 flex-flow: column wrap 的時候,你應該會覺得好奇,為何 break-after 會對他有效?其實,這一系列的設定,可以用到的地方很多,諸如:

  • 一般內容中斷
  • 列印頁面中斷
  • column 中斷
  • 多行區塊( Multi-column regions )中斷

所以說,以剛剛的例子來說,我們就再也不需要 flex-basis: 100% 的設定了,我們也不需要 最大寬度最大高度 的設定了,以 row 為例子:

.flex-3 {
    break-after: always;
}

column 為例子:

.flex-3 {
    break-after: always;
}

對,你沒看錯,我沒有打錯,就只要設定 break-after: always 就好了,這樣你就可以任意的中斷在你想要中斷的位置上。

明眼人應該注意到了,使用 flex-flow: column wrap 搭配 break-after: always ,基本上相當接近瀑布流的呈現。而且我可以兩邊任意不等高,如果再加上 order 來調動順序,可以做到最接近於瀑布流的樣式。

當然,最終你還是要決定在哪個區塊加上 break-after: always ,這個應該無法靠純 CSS 解決。

很可惜,由於 CSS Regions Level 1 基本上 WD 都已經六年過去了,看起來要推出不知道還要等多久。至於 CSS Multi-column Layout Module Level 1 ,基本上跟我們用 Flexbox 來做 column 其實是兩件事情。

所以,除非 Chrome/Safari/Opera 有意把 break-* 系列讓他支援 Flexbox 的 columnrow ,不然基本上就只能 Firefox 自己玩玩就好了。


小結

JavaScript 套件還是加減用,等瀏覽器支援跟等 W3C,我想我回鄉種田可能比較好。

Hina Chen
偏執與強迫症的患者,算不上是無可救藥,只是我已經遇上我的良醫了。
Taipei