【CSS】519- grid-auto-flow深入理解

作者:張鑫旭

原文:https://www.zhangxinxu.com/wordpress/2020/01/css-grid-auto-flow/

一、grid-auto-flow出現的背景

Grid布局要想玩得666,grid-auto-flow是繞不開的需要深度掌握的CSS屬性。

Grid布局就像農村土改分土地。

政府會把土地分成一塊一塊的,然後這一塊是張三的,那一塊是王二的。

這個劃分就是通過grid-template屬性。

比方說:

.fileds {      display: grid;      grid-template: 1fr 1fr / 1fr 1fr;  }

就是把這塊土地分成兩行,兩列4塊田地。

然而應用到具體的場景,事情往往就出乎意料的複雜。

例如:

  1. 如果日後村裡搬遷了幾戶新住戶,那該怎麼劃分?
  2. 4塊田地分給張三和王二,你說是橫的兩塊地給張三呢,還是豎的兩塊地給張三?
  3. 孤寡老人李大爺過世了,他的田地是否可以分給後面的人?

這些意料之外的場景單靠grid-template屬性肯定捉襟見肘,此時,就需要grid-auto-flow屬性出馬。

grid-auto-flow屬性可以定義Grid布局中每一個Grid子項「自動流動」狀態。

二、了解grid-auto-flow的語法

grid-auto-flow: [ row | column ] || dense

因此,下面這些寫法都是合法的:

grid-auto-flow: row;  grid-auto-flow: column;  grid-auto-flow: dense;  grid-auto-flow: row dense;  grid-auto-flow: column dense;

其中:

  • row
  • 多的格子一行一行陳列。默認值。
  • column
  • 多的格子一列一列排列。
  • dense
  • 多的格子空白填充。

各個關鍵字值具體什麼意思,我們還得看案例才能明白。

三、row和column屬性值基礎

1. 默認值row

從天上掉下來一個寶貝容器,定睛一看,容器元素標籤名是zxx-grid,好巧,正好應用的CSS聲明就是display:grid,如下:

zxx-grid {      display: grid;  }

然後很巧的是還有若干子元素,如下:

<zxx-grid>      <zxx-item>格子1</zxx-item>      <zxx-item>格子2</zxx-item>      <zxx-item>格子3</zxx-item>      <zxx-item>格子4</zxx-item>      <zxx-item>格子5</zxx-item>      <zxx-item>格子6</zxx-item>      <zxx-item>格子7</zxx-item>      <zxx-item>格子8</zxx-item>      <zxx-item>格子9</zxx-item>  </zxx-grid>

<zxx-grid>元素除了應用了display:grid沒有任何其他CSS,因此,所有的子元素的grid-auto-flow「自動流動」的狀態是其默認值。

grid-auto-flow的默認值是rowrow的中文意思是「行」,也就是所有的子元素一行一行顯示,優先水平排列。

於是,最終的網格輪廓結構會是下圖這樣:

更進一步

如果我們使用grid-template屬性指定Grid的列數為2列,如下:

zxx-grid {      display: grid;      grid-template: auto / 1fr 1fr;      line-height: 40px;  }

大家就會看到Grid子元素的排列流向是下面這樣的,先是水平排列,然後再到下一行再水平排列,一直到最後不斷循環:

大家可能覺得理所當然就應該這樣排列,太天真了!

大家稍後對比column屬性值的效果就知道Grid子項的排列順序並不是一成不變的了。

2. 關鍵字值column

如果我們設置grid-auto-flow的屬性值是columncolumn的中文意思是「列」,則表示「自動流動」狀態的子元素全部都一列一列顯示。

假設容器元素CSS如下:

zxx-grid {      display: grid;      grid-auto-flow: column;  }

則幾個子元素的布局效果會是下面截圖這樣,全部縱排了:

發現沒有?使用grid-auto-flow屬性可以非常簡單實現子元素個數不固定的等分佈局效果,就是值設置為column就可以了。

更進一步

這裡,我們同樣指定我們的Grid布局是2行,CSS代碼如下:

zxx-grid {      display: grid;      grid-template-rows: 1fr 1fr;      grid-auto-flow: column;      line-height: 40px;  }

大家就會發現Grid子元素的排列流向和row屬性值就不一樣了:

是優先垂直方向排列,然後再到下一列往下排列,一直循環往複到最後一列。

由此,我們可以得出結論,grid-auto-flow屬性會影響Grid布局的排列順序。

3. 排序與布局實例

有人在微信粉絲群裏面問了下面這個問題,實現下圖所示布局:

這個布局效果,最簡單的方法就是用浮動3個圖片都設置float:left就可以了,就這麼簡單。

但是如果我們這裡的圖片不是3個,而是5個(如下圖),那麼浮動就無能為力了。

此時,則可以試試Grid布局,讓第一個子項橫跨左側兩個格子,然後後面4個格子按照grid-auto-flow:column的流向排列即可。

HTML代碼如下:

<zxx-grid>      <zxx-item><img src="./xugou-1.jpg"></zxx-item>      <zxx-item><img src="./xugou-2.jpg"></zxx-item>      <zxx-item><img src="./xugou-3.jpg"></zxx-item>      <zxx-item><img src="./xugou-4.jpg"></zxx-item>      <zxx-item><img src="./xugou-5.jpg"></zxx-item>  </zxx-grid>

CSS代碼如下:

zxx-grid {      display: grid;      grid-auto-flow: column;      grid-template: 1fr 1fr / 1fr 1fr 1fr;      grid-template-areas: "a b d" "a c e";      grid-gap: 6px;  }  zxx-item:first-child {      grid-area: a;  }

則有如下圖所示的布局效果:

而且整個布局是彈性變化的,如下視頻所示(不動可以點擊播放):

對吧,布局效果還是很酷的。


最後,測試下grid-auto-flow:column在這個例子中所起的作用,我們打開控制台,注釋掉grid-auto-flow:column這句CSS聲明,則我們會得到下圖所示的布局效果,圖2到圖5都是水平平鋪呈現,這個效果Float浮動就可以實現。

對應的Demo頁面

上面的例子有專門的demo演示頁面,您可以狠狠的點擊這裡:grid布局實現圖像A|B+C布局demo

//zxx: Chrome瀏覽器的圖片height:100%似乎有bug,Firefox瀏覽器沒有這個問題

四、row和column屬性值深入

第三小節演示的幾個案例都是我們所有的格子都已經規定好的,但是實際開發,Grid格子的數量可能是動態的,無論是grid-template還是grid-template-areas往往就沒法兼顧全部的子元素,此時,Grid布局又該如何表現?

為了更進一步展示關鍵字值column的效果,我們來看一個稍微再複雜一點的例子:

我們設定網格模板是 2 x 2,也就是前4個元素按照指定網格陳列,後面的子元素auto-flow自由流動。相關CSS如下:

zxx-grid {      display: grid;      grid-template: 1fr 1fr / 1fr 1fr;      grid-auto-flow: column;  }

則最終效果如下:

為什麼會有上圖這樣的效果呢?

首先大家一定要搞清楚一點,1fr 1fr並不等於50% 50%,雖然很多時候表現是一樣的,但只是grid格子正好和設定數量一致的時候。1fr 1fr更準確的含義是,可用剩餘空間分別50% 50%

在本例中,格子5~格子9屬於「放任自流」的盒子,因為grid-template:1fr 1fr / 1fr 1fr只指定了4個格子的尺寸,那剩餘的5個格子怎麼辦呢?

那就是grid-auto-flow屬性大發神威的時候。

這裡grid-auto-flow設置的屬性值是column,也就是沒有指定模板的剩餘5個格子垂直按需排列。如下圖標註:

由於沒有指定寬度,因此,格子的尺寸就fit-content,根據內容的尺寸來。


在實際開發的時候,我們往往只要指定好垂直列的個數和寬度分配比,無論多少個grid子元素,都會依次排列好,其最底層最根本的原因其實是grid-auto-flow:row在起作用。

例如:

zxx-grid {      display: grid;      grid-template-columns: 1fr 1fr;  }

效果是下面這樣,9個格子了,一個一個排的非常整齊,因為「自動流動」默認是水平的。

五、不太好懂的dense屬性值

最後,再講講dense屬性值。

dense單從字面含義是不太好理解的,dense的中文意思是「密集的」,「稠密的」,「濃密的」。

密集的是樹林,稠密的是奶昔,濃密的是頭髮,跟Grid布局好像不搭邊吧。

實際上,這裡dense應該用一種更感性的方式去體會這個詞的含義。

在本文一開始提到了一個引子「孤寡老人李大爺過世了,他的田地是否可以分給後面的人?」

比如說村裡9戶人家,每一戶都分得了一塊田地,大家的田地都是緊密相連的。

結果孤寡老人李大爺和王大爺相繼去世。於是,就有兩塊田地是空缺的,因為有空缺,所以這個時候,如果我們使用無人機從上方拍一個照片,則田地是稀疏的,不是緊密的。

這個時候,村裡又來了兩戶新人家,也要分田地。

如果是dense,則優先分配之前李大爺和王大爺留下的空缺的土地,因為這樣會讓整片田地沒有空缺,是「密集的」,「緊密的」。

grid-auto-flow:dense也是類似的意思。

dense理解案例

我們可以人為製造出稀疏布局結構(前面幾個Grid格子留空),代碼如下:

zxx-grid {      display: grid;      grid-template-columns: 1fr 1fr;  }  zxx-item:first-child {      grid-column-start: 2;  }

效果如下,第一個格子空缺了:

如果我們希望第一個格子被充分利用,讓整個排列是緊密相連的,則可以使用dense關鍵字值。

zxx-grid {      display: grid;      grid-template-columns: 1fr 1fr;      grid-auto-flow: dense;  }  zxx-item:first-child {      grid-column-start: 2;  }

此時瀏覽器的渲染表現是怎樣的呢?請看下圖:

原本流出來的第1個格子的窟窿,被格子2給填上了。

這個Grid布局又是「緊密的」了,這就是dense屬性的作用。

dense實際開發的時候有什麼作用呢?

比方說我希望某兩個相鄰的格子視覺順序和DOM順序是相反的,也就是兩者調換位置,則可以使用dense實現。

其他

dense可以和row以及column這兩個關鍵字只是同時使用,例如:

grid-auto-flow: row dense;  // 等同於dense  grid-auto-flow: column dense;

六、最後的總結么么噠

本文雖然只有5000字,不過花的時間還挺多,算上今天,斷斷續續寫了一周。

為什麼這篇文章跨度時間比較長呢?除了用心做demo,用心創作外,還有其他兩方面主要原因:

  1. 邊研究邊測試邊寫,結果發現了自己一開始想當然的錯誤理解,然後不少內容重新組織推翻重來。因此可見,寫文章其實對深度學習、了解細節,理清思路非常有幫助,是非常好的一種學習方法。
  2. 現在上年紀了,體力沒以前好了。以前都是寫到1點,這種程度的文章2~3天就完成。現在頂不住了,12點左右就困得不行,關機睡覺去了,所以寫作時間跨度就更長了。

CSS文章已經連更十幾篇了,小本本上還有好幾篇CSS文章要寫的。

光CSS基礎API只是更新迭代的東西就這麼多,再考慮到吃了激素一路狂奔年年暴走的ES規範,現在的前端開發想成為通才基本上不可能了,只能選擇合適的領域,一點一點發光發熱吧。

哎呀呀,就說這麼多了。

用心之作,歡迎分享!

行文匆忙,如果文章有表述不準確的地方,歡迎指正!