你以為網格格線告一個段落後,我會開始講網格單元嗎?當然不是啊,我們網格容器都還沒講完呢。剩下一點小東西稍微交代一下就可以了。
放心雷沒有很多。
容器中的隱性軌道設定
前一篇提到隱性格線,在容器中,也有兩個樣式設定是 專門 給隱性軌道使用的。
樣式 | 預設值 |
---|---|
grid-auto-rows |
auto |
grid-auto-columns |
auto |
由於兩個設定的可使用數值都一樣,我直接拿出來外面講比較快,
- 相對尺寸
%
fr
min-content
max-content
auto
minmax()
fit-content()
以上這幾種都是給隱性軌道使用,我快速舉個例子,
.grid-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: 1fr;
grid-auto-rows: 300px;
}
<div class="grid-container">
<div class="grid-item">1</div>
<div class="grid-item">2</div>
<div class="grid-item">3</div>
<!-- 實際上只設定了一列,但這邊是第二列資料 -->
<div class="grid-item">4</div>
<div class="grid-item">5</div>
<div class="grid-item">6</div>
</div>
同樣的道理 grid-auto-columns
也是一樣的操作方式,具體案例我就不再寫一次了。
軌道尺寸詳解
回到 Grid 容器軌道上,我們知道軌道除了可以設定常見的單位尺寸外,現在又多了 fr
可以使用。然而,在 Grid 容器當中,我們還有這些方法可以操作,
CSS 關鍵字/運算方法 | 說明 |
---|---|
min-content |
設定在軌道中所有最小內容單元的尺寸中,取最大的尺寸 |
max-content |
設定在軌道中所有最大內容單元的尺寸中,取最大的尺寸 |
fit-content(limit) |
接收一個限制數值 limit ,並套入公式 max(minimum, min(limit, max-content)) ,其中 minimum 代表了軌道中的最小尺寸,通常會使用 auto 關鍵字,但多數情況下會符合 min-content 的尺寸。 |
minmax(min, max) |
定義一組最小、最大值的區間來當作尺寸,他是一個彈性的範圍。如果兩個數值寫反了,會直接取用最小值。 |
其中 min-content
, max-content
在實務上比較難以看出效果,針對官方所提出的演算法則來看,其實也看不太出端倪。我舉一個稍微複雜一點的設定,然後我們來看看運作邏輯。
grid-template-columns: 200px 1fr max-content minmax(min-content, 100px);
假設我們的 Grid 容器有這樣的欄設定,那麼計算順序為,
- 從 容器邊緣 起算。這很重要,因為 不是每個人的邊緣都是左上角。
- 邊緣畫出第一條線,第一個欄位是
200px
寬。 - 畫出第二條線,第二個欄位是使用剩餘空間計算
1fr
的尺寸。 - 畫出第三條線,第三個欄位是在該軌道中任何一個 Grid 單元,取最大尺寸。
- 畫出第四條線,第四個欄位是在該軌道中任何一個 Grid 單元,從最小尺寸中取一個最大值,當作最小值,並且以
100px
當作最大值來做範圍運算。
從最小尺寸中取一個最大值 到底是什麼神邏輯?
我用實際案例解釋給你看,首先我們先設計一個第一欄使用 min-content
的容器,
.grid-container {
display: grid;
grid-template-columns: min-content repeat(2, 1fr);
grid-template-rows: repeat(3, 1fr);
width: 100%;
height: 500px;
}
他是一個 3x3
的容器,其他兩個欄位都使用彈性空間,列的部分也全部使用彈性空間。然後我們針對第一欄的部分來放一些資料,也就是我們的 1
, 4
, 7
這三個地方要放一點特別的資料進去。
<div class="grid-container">
<div class="grid-item">
<p>放一點特別的資料進去</p>
</div>
<div class="grid-item">2</div>
<div class="grid-item">3</div>
<div class="grid-item">
<p>放一點特別的資料進去</p>
</div>
<div class="grid-item">5</div>
<div class="grid-item">6</div>
<div class="grid-item">
<p>放一點特別的資料進去</p>
</div>
<div class="grid-item">8</div>
<div class="grid-item">9</div>
</div>
會後他會變成這樣,
你的 Grid 單元內容超出 height: 500px
的設定了,對,這算是 min-content
的一個特別的地方,他所謂的 最小內容 是利用文字特性來做計算的。所以,當你使用了這項設定,那們請確保你的文字內容真的是你想要呈現的方式。
另外,你的容器若沒有特定尺寸,基本上 Grid 單元還是以填滿整個 Grid 容器為目標去填滿的。所以如果不指定尺寸,基本上不會有上面 Grid 單元超出容器的狀況。
接著,我們來加一點料進去,剛剛有說了,他是取用 在軌道中所有最小內容單元的尺寸中,取最大的尺寸 來用,那麼如果我們放了一個比較困難的文字進去,
<div class="grid-container">
<div class="grid-item">
<p>放一點特別的資料進去</p>
<p>Internationalization and localization</p>
</div>
<div class="grid-item">2</div>
<div class="grid-item">3</div>
<div class="grid-item">
<p>放一點特別的資料進去</p>
</div>
<div class="grid-item">5</div>
<div class="grid-item">6</div>
<div class="grid-item">
<p>放一點特別的資料進去</p>
</div>
<div class="grid-item">8</div>
<div class="grid-item">9</div>
</div>
那麼他的結果大家可以猜到了嗎?
那這樣跟
max-content
到底有什麼差別?
別急,我們把 Grid 容器換成 max-content
再來看看到底發生什麼事情,
.grid-container {
display: grid;
grid-template-columns: max-content repeat(2, 1fr);
grid-template-rows: repeat(3, 1fr);
}
其他的內容我們都不更動,我們來看看 max-content
怎麼呈現我們的結果,
這樣可以稍微理解 min-contetn
與 max-content
之間的差異了吧。當然,這種演算方式並無法避開區塊元件的佔用尺寸問題。換句話說,
當 Grid 單元中有區塊元件(Box Module)並指定尺寸,且超出了
min-content
的計算尺寸,那麼,這個地方的min-content
結果就會跟max-content
沒什麼太大區別。就如同上面所描述的,所謂min-content
是 在軌道中所有最小內容單元的尺寸中,取最大的尺寸 來使用,所以區塊元件佔用,又超出計算尺寸,就會被當成是min-content
的最終結果。
舉個例子給大家看看,這次就不附上圖片了,請大家自行想像一下。
<div class="grid-container">
<div class="grid-item">
<p>放一點特別的資料進去</p>
</div>
<div class="grid-item">2</div>
<div class="grid-item">3</div>
<div class="grid-item">
<p>放一點特別的資料進去</p>
<img src="...">
</div>
<div class="grid-item">5</div>
<div class="grid-item">6</div>
<div class="grid-item">
<p>放一點特別的資料進去</p>
</div>
<div class="grid-item">8</div>
<div class="grid-item">9</div>
</div>
.grid-container {
display: grid;
grid-template-columns: min-content repeat(2, 1fr);
grid-template-rows: repeat(3, 1fr);
}
.grid-item img {
display: block;
width: 300px;
height: 300px;
object-fit: cover;
}
在這種狀況,無論你是使用 min-content
或 max-content
,你的第一個欄尺寸基本上就是 300px
,除非你的內文計算尺寸能超過,否則這個 300px
就會是最小(也可能是最大)尺寸。
fit-content(limit)
其實我們把他拆解出來會比較容易理解,
max(minimum, min(limit, max-content))
其中 max-content
上面有解釋過了,我們只要專注在 limit
與 minimum
這兩個地方就好。首先,limit
是你傳進去的數值,所以如果我們這樣寫,fit-content(300px)
那他就等同於,
fit-content(300px) = max(minimum, min(300px, max-content))
問題來了,那個 minimum
是什麼呢?在多數情況下,他是使用 auto
這個關鍵詞來讓裝置自動決定運算尺寸,通常,在最常見的情況下,他會是 min-content
的數值。會不會有意外?我不能跟你保證永遠不會。因為在 auto
的定義裡面,還是有一些貓膩。
總歸一句,這邊的 minimum
所使用的 auto
會採用的是取最小值的部分,而在 Grid 自動取得最小值(如 min-content
的演算法)所使用的大方向有這三種順序,
- 採用特定(指定)尺寸(specified size)
- 採用演算(轉換)尺寸(transferred size)
- 採用內容(文本)尺寸(content size
- 如果以上都沒有則為
0
所以,我們剛剛在解釋 min-content
的範例中,我們放了一張圖片,那張圖片對於 Grid 單元就屬於轉換內容的尺寸,符合第 2 點的演算方式。所以你會取得該單元的 min-content
就是內容佔有的尺寸。
如果你不寫 fit-content()
的話,是可以避開 min-content
的問題。就是把他拆成上面的公式就好了,如果你真的想要使用,又不想踩到 min-content
可能會發生的狀況的話。
總結一句話,所謂的 fit-content()
的意思是,
類似
min-content
但如果比min-content
還大就取比較大的。
然後如果掉入min-content
的問題,就等於max-content
。
minmax(min, max)
我們在 Part 7 提到 1fr
其實會等於 minmax(auto, 1fr)
大家還記得嗎?
如果忘了可以回去 Part 7 的文章 重看一次。
然後這邊的 auto
一樣會有剛剛 fit-content()
的問題,就不再贅述。這個 CSS 運算方法其實很容易理解,他就只是一個取出一個範圍尺寸,然後依照這個範圍尺寸變動欄或者是列的尺寸。對於需要彈性應用,但又不想過大(或過小)的網格單元來說很方便。
需要注意的點就是,
- 最大、最小值不要寫反,寫反等同於
minmax(min, min)
- 容器單元指定超出此範圍的尺寸不干擾網格系統
- 容器單元指定超出此範圍的尺寸不干擾網格系統
- 容器單元指定超出此範圍的尺寸不干擾網格系統
第二點很重要所以說三次!
容器單元指定超出此範圍的尺寸不干擾網格系統
舉例來說,
.grid-container {
display: grid;
grid-template-columns: repeat(3, minmax(300px, 1fr);
grid-template-rows: repeat(3, 1fr);
}
.grid-item:first-child {
width: 1200px;
}
最終你會得到這樣的結果,
所以,當你在 Grid 容器中使用 minmax()
時,請特別注意你的 Grid 單元尺寸的狀況,否則他是不會排在位置上的,另外,如果你的 1fr
有與 minmax()
混用的情況,請也特別留意 Grid 單元尺寸,因為在這個時候所謂的 剩餘空間 可能跟你想像的不太一樣。
容器單元流向
官方對於此有一份演算法說明,
我覺得比較詫異的是,官方對於容器單元流向,僅說明是 針對 隱性軌道的網格單元,但是實際上是整個容器都受用,無論你是不是隱性軌道。但,其實這樣說起來也是合理的,你總不可能一般軌道是一種流向,然後隱性軌道是另外一種流向吧。
控制容器單元流向只有一個樣式,
樣式 | 可用值 | 預設值 |
---|---|---|
grid-auto-flow |
[row, column] , dense |
row |
row
跟 column
就很單純,可以想成對於欄或列的 Grid 單元 優先 擺放流向。以預設值 row
來說,他就是由左至右(非 RTL 文本模式),由上到下的狀況來排列。而換成 column
則是由上到下,由左至右,這種方式跟 Flexbox 在交換主要軸、交叉軸的情況很相似。
至於 dense
這個關鍵字,則是可以單獨,或搭配 row
, column
使用的,例如,
.grid-container {
display: grid;
grid-auto-flow: dense;
/* 或是 */
grid-auto-flow: row dense;
}
所謂的 dense
的演算方式是採用密集(緊湊)排列法,當你的 Grid 單元尺寸不同時,預設的排社方式是遇到空間不足就會往下(row
方向)或往右(column
方向)繼續排列,這個時候,在某些區域就會出現空白的區塊,舉例來說,
.grid-container {
display: grid;
grid-template-rows: repeat(3, 1fr);
grid-template-columns: repeat(3, 1fr);
grid-auto-flow: row dense;
}
.grid-item:nth-of-type(1),
.grid-item:nth-of-type(2) {
grid-column: auto / span 2;
}
此時當你使用 dense
的時候,他就會變成,
這種緊湊排版很類似以前流行過的 Masonry Layout,也就是我之前寫過的 瀑布流難題,這個基本上在 Grid 上面可以得到一個還算不錯的解法。
容器單元的填滿
最後我們來聊填滿這件事情,大家前面應該看了非常多 repeat()
的使用,對,最後我們來聊一下關於 repeat()
這件事情。他的目的就是重複你所設定的軌道,除了重複次數以外,他還有兩個特殊關鍵字可以使用。
重複目標 | 使用方式 |
---|---|
網格軌道 | repeat(次數, <軌道尺寸> 或加上 <格線名稱>) |
固定尺寸 | repeat(次數, <固定尺寸> 或加上 <格線名稱>) |
auto-fill |
repeat(auto-fill, <固定尺寸> 或加上 <格線名稱>) |
auto-fit |
repeat(auto-fit, <固定尺寸> 或加上 <格線名稱>) |
軌道尺寸 與 固定尺寸 僅差在軌道尺寸可以使用 fit-content()
而固定尺寸至多能使用 minmax()
,這是兩者唯一差別。所以以下寫法基本上都是合法的,
.grid-container {
grid-template-columns: repeat(3, 1fr);
grid-template-columns: repeat(3, 100px);
grid-template-columns: repeat(3, [foo] 100px [boo]);
grid-template-columns: repeat(3, minmax(auto, 1fr));
grid-template-columns: repeat(3, minmax(100px, 300px));
grid-template-columns: repeat(3, minmax(auto, 1fr));
grid-template-columns: repeat(3, fit-content(100px));
grid-template-columns: repeat(auto-fit, 100px);
grid-template-columns: repeat(auto-fill, minmax(auto, 200px));
}
關於 auto-fill
與 auto-fit
兩者的差異,在於是否 填滿 容器。我們直接用圖片來解釋會比較容易理解,
兩者的差異在於,
auto-fill
會盡可能在網格容器中填滿所設定的網格軌道auto-fit
網格軌道則是僅滿足需要用到的網格單元
共同點是,你的 Grid 容器隨時都有可能產生剩餘空間。
小記
其實網格容器還是有一些小地方可以講,但那個實在太冷門,我留到最後再提好了。
目錄與小節:
[CSS] Flex/Grid Layout Modules, part 0