在 CSS 中有很多種類型的函數,其中可用于尺寸屬性的函數主要有 calc()
、min()
、max()
、clamp()
等。這些 CSS 函數都可用來設置網格軌道尺寸,除此之外,還有一些專門用于設置網格軌道的函數,比如 repeat()
、minmax()
和 fit-content()
等。接下來,我們主要來看看這些函數是如何用于設置網格軌道尺寸的,它們給網格布局又能帶來哪些不一樣。
repeat() 函數
我們先從 repeat()
函數開始。
在前面介紹網格軌道尺寸設置的課程中,常會看到給 grid-template-columns
、grid-tempalte-rows
、 grid-auto-columns
和 grid-auto-rows
設置多個相同的長度值,比如:
.container {grid-template-columns: 1fr 1fr 1fr;
}
表示列軌道設置了三個相同的值。針對于這樣的場景,網格布局中提供了一個 repeat()
函數,可以讓上面的代碼變得更簡潔:
.container {grid-template-columns: 1fr 1fr 1fr;/* 等同于 */grid-template-columns: repeat(3, 1fr);
}
網格中的 repeat()
函數主要用來設置網格軌道列表(<track-lists>
)的重復片段,允許以更緊湊的形式寫入大量顯示重復模式的網格軌道(列或行)。該函數可以用于 grid-template-columns
和 grid-template-rows
屬性,用來設置網格軌道尺寸大小,但它不能用于 grid-auto-rows
和 grid-auto-columns
。
repeat()
函數具有一定的語法規則,它接受兩個參數:
- 第一個參數表示重復的次數,比如
repeat(3, 1fr)
中的3
,該參數除了可以是正整數之外,還可以是auto-fit
和auto-fill
兩關鍵詞。 - 第二個參數是一個長度列表值,即重復的網格軌道的列表值,比如
repeat(3, 1fr)
中的1fr
;另外該參數的值還可以是一個復合值,比如repeat(3, 1fr 20px [col])
中的1fr 20px [col]
。
我們通過幾個示例來向大家展示 repeat()
函數的幾種常用的使用方式。先從最簡單的開始,即:
.container {grid-template-columns: 1fr 1fr 1fr 1fr;/* 使用 repeat() 函數 */grid-template-columns: repeat(4, 1fr);
}
Demo 地址: https://codepen.io/airen/full/xxjNjeL
repeat()
函數中的第二個參數還可以是一個列表值,比如 1fr 200px
:
.container {grid-template-columns: repeat(3, 1fr 200px);/* 等同于 */grid-template-columns: 1fr 200px 1fr 200px 1fr 200px;
}
代碼中的 repeat(3, 1fr 200px)
意思是 1fr 200px
會重復 3
次,相當于創建了一個六列的網格:
Demo 地址: https://codepen.io/airen/full/RwymJZO
repeat()
函數的第二個值除了可以是網格軌道列表值之外,也可以顯式給網格線命名,比如:
.container {grid-template-columns: repeat(3, 1fr [col]);/* 等同于 */grid-template-columns: 1fr [col] 1fr [col] 1fr [col];
}
要是在 repeat()
函數中重復網格線名稱的話,結束的網格線名稱最終會與下一條開始網格線名稱共享同一個網格線名稱:
Demo 地址:https://codepen.io/airen/full/WNJBKVg
如果你在使用 repeat()
給網格軌道設置尺寸時需要顯式命名網格線名稱,還可以像下面這樣使用:
.container {grid-template-columns: repeat(3, [col-start] 1fr [col-end]);/* 等同于 */grid-template-columns: [col-start] 1fr[col-end col-start] 1fr[col-end col-start] 1fr[col-end];
}
另外,可用于設置網格軌道尺寸的值,都可以被用于 repeat()
函數的第二個參數,比如:
.container {grid-template-columns: repeat(3, minmax(min(300px, 100%), 1fr));/* 等同于 */grid-template-columns: minmax(min(300px, 100%), 1fr) minmax(min(300px, 100%), 1fr) minmax(min(300px, 100%), 1fr);
}.container {grid-template-columns: repeat(3, min-content auto max-content);/* 等同于 */grid-template-columns: min-content auto max-content min-content auto max-contentmin-content auto max-content;
}
但需要注意的是,repeat()
函數中不能嵌套 repeat()
函數!
auto-fill vs. auto-fit
你可能已經發現了,前面幾個示例中 repeat()
函數的第一個參數都是整數值。一般在你已經知道網格軌道要重復的次數時才用,但很多時候,你可能并不知道網格軌道重復的數量,更希望的是它能自動匹配。
慶幸的是,repeat()
函數的第一個參數除了可以接受一個整數值之外,還可以接受 atuto-fit
和 auto-fill
兩個關鍵詞。它們會告訴瀏覽器處理網格軌道的大小和斷行(或斷列),以便當容器空間不足以容納元素時,元素會自動換行(或列)而不會造成溢出。但 auto-fill
和 auto-fit
兩者之間還是有一些細微差異的。
auto-fill
:在同一行中填充盡可能多的列。因此,只要能容納新的列,就會自動創建隱式列,因為它試圖在同一行中填充盡可能多的列。新添加的列(隱式列)可以是空的,但是它們仍然會在行中占據指定的空間。auto-fit
:將當前可用的列擴展到空間中,以便它們占用容器可用空間。當容器有可用空間時,瀏覽器會將可用空間均分給列,讓列自動變寬填滿整個容器;當容器可用空間為負值時,會另起一行排列。
簡單地說,auto-fit
將擴展網格項目以填補可用空間,而 auto-fill
不會擴展網格項目。相反,auto-fill
將保留可用的空間,而不改變網格項目的寬度 。
在實際使用過程中,網格容器中有多個和僅有一個網格項目時,使用 auto-fill
與 auto-fit
的差異:
上面兩張圖展示了 auto-fit
和 auto-fill
在網格布局中的差異。那它們兩者又是如何工作的呢?我們以一個實例來向大家介紹 auto-fit
和 auto-fill
是如何工作的。
<div class="container"><div class="item"></div><!-- 此處省略兩個 Item --><div class="item"></div>
</div>
.container {--width: 100%,--auto-size: auto-fit;display: grid;grid-template-columns: repeat(var(--auto-size), 120px);gap: 10px;width: var(--width); /* 它的父元素 width = 1000px */padding: 10px;
}
示例中網格容器的寬度是 1000px
,并且設置了 10px
的內距(padding
),grid-template-columns
指定的列網格軌道尺寸是 120px
,網格溝槽(列網格軌道之間的間距)是 10px
。
你會發現,repeat()
函數的第一個參數不管是 auto-fit
還是 auto-fill
,瀏覽器都會根據相關的參數(比如網格容器的寬度、網格軌道尺寸和網格溝槽等)創建出最適合于網格容器可用空間的網格列軌道數量,即,在保證網格項目不溢出網格容器之下,創建最多數量的網格列(或行)軌道 。
網格容器寬度 = 網格列軌道尺寸 × 網格列軌道數量 + (網格列軌道數量 - 1) × 網格溝槽
980 = 120px × ? ( ? - 1) × 10
瀏覽器計算出這個“?
” 大約會是 7.66667
,所以瀏覽創建了一個七列的網格。不同的是 auto-fit
會把空的網格軌道折疊在一起(空網格軌道是指沒有放置網格項目的網格軌道)。折疊的軌道尺寸大小會被視為 0px
。瀏覽器為了找到自動重復的軌道數,會將軌道尺寸限制為用戶代理指定的值(比如 1px
),來避免被零除。
auto-fill
則不會將創建的空網格軌道折疊在一起:
repeat()
函數中使用 auto-fit
或 auto-fill
關鍵詞替代重復的次數時,又被稱為自動換行 。當網格容器無法容納網格軌道時(有網格項目的),就會自動創建新的一行:
Demo 地址:https://codepen.io/airen/full/PoevxOp
雖然在 repeat()
函數中使用 auto-fit
或 auto-fill
都可能創建盡可能多的列,但每個網格軌道的尺寸是固定的,它并不是一個自動尺寸。不過,可以將 fr
單位值和 minmax()
函數結合在一起,讓網格軌道尺寸是自動的 ,即網格軌道尺寸是自動匹配的(在一個范圍內)。
把上面示例稍微調整一下,將 repeat(var(--auto-size), 120px)
中的 120px
替換成 minmax(120px, 1fr)
,即:
.container {--width: 100%,--auto-size: auto-fit;display: grid;grid-template-columns: repeat(var(--auto-size), minmax(120px, 1fr));gap: 10px;width: var(--width); /* 它的父元素 width = 1000px */padding: 10px;
}
auto-fit
時,創建的重復軌道尺寸是 0
,網格軌道的尺寸會介于 120px ~ 1fr
之間,最小是 120px
,最大是 1fr
,而且 1fr
會根據網格容器可用空間計算出網格軌道尺寸。由于創建的重復軌道尺寸是 0
,所以網格容器可用空間更大(1000px - 10px × 2 - 10px × 3 = 950px
),對應的 1fr = 1 / 4 = 25% × 950px = 237.5px
,所以你將看到的網格項目被拉伸了:
auto-fill
創建的重復軌道尺寸也是 minmax(120px, 1fr)
,而且不會被折疊,所以網格容器的可用空間分的等份就更多(因為創建的三個空網格軌道,它位置占著),即 7
個 fr
。同時網格容器可用空間也更小 (1000px - 10px × 2 - 10px × 6 = 920px
),對應的 1fr = 1 / 7 = 14.28% × 920px = 131.43px
,即網格軌道尺寸是介于 120px ~ 1fr
(相當于 120px ~ 131.43px
)之間:
Demo 地址: https://codepen.io/airen/full/mdLZwey
你要是將 repeat()
函數和 minmax(min,max)
、1fr
和 auto-fill
(或 auto-fit
)結合起來,可以很容易幫我們實現像下圖這樣的響應式布局效果:
實現上圖這樣的效果,代碼很簡單:
.container { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 20px;
}
Demo 地址: https://codepen.io/airen/full/mdLZMBp
這種布局技術也被稱為 RAM(Repeat, Auto, Minmax) 布局,一種無需依賴任何 CSS 媒體查詢特性的響應式布局。
minmax() 函數
minmax()
函數是用于設置網格軌道尺寸的另一個函數,它可以用于 grid-template-columns
、 grid-template-rows
、grid-auto-columns
和 grid-auto-rows
屬性上。該函數可以接受兩個參數值,即 MIN
和 MAX
。每個參數都可以是:
<length-percentage>
值,比如px
和%
;<flex>
值,比如fr
;- 關鍵詞,比如
auto
、min-content
、max-content
; - 函數表達式,比如
min()
、max()
、clamp()
和clac()
。
minmax(MIN, MAX)
可以輸出一個范圍值,它定義了一個大于或等于 MIN
值且小于或等于 MAX
值的尺寸范圍 。簡單地說,minmax(MIN, MAX)
函數將返回MIN ~ MAX
范圍中的一個值。我們可以像下面這樣使用 minmax(MIN, MAX)
函數:
grid-template-columns: minmax(200px, 300px);
grid-template-columns: minmax(min-content, 320px);
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
grid-template-columns: minmax(min(200px, 100%), 1fr);
grid-template-columns: minmax(300px, 50%);
接下來,我們花點時間來看看, minmax(MIN, MAX)
函數取不同類型值是如何工作的。
先來看最簡單的 <length>
值類型,比如:
.container {grid-template-columns: minmax(100px, 220px) 1fr 1fr;
}
Demo 地址:https://codepen.io/airen/full/JjvQrJp
使用 minmax(100px, 220px)
指定網格第一列的列寬保持在 100px
至 220px
之間,隨著網格容器尺寸的變化,該列的寬度也會有改變,但總是在這兩個值的范圍內變化:
- 當網格容器寬度足夠寬時,
minmax(100px, 220px)
返回的值是220px
,即第一列的網格軌道寬度是220px
(MAX
的值)。 - 當網格容器寬度調到很小時(比如
222.5px
),minmax(100px, 220px)
返回的值是100px
,即第一列的網格軌道寬度是100px
(MIN
的值)。 - 另外,
minmax(100px, 220px)
還會返回一個100px ~ 220px
之間的值,比如當容網格容器寬度是300px
時,minmax(100px, 220px)
返回的值就是177.34px
。
正如你所看到的,示例中網格的第二、第三列的收縮和擴展會根據網格容器可用空間變化(因為它們的軌道寬度設置的值是 1fr
),但第一列網格軌道的寬度總是保持在 100px
至 220px
之間,最小不小于 100px
,最大不大于 220px
。
我們把上面這個示例中的 220px
換成一個<percentage>
(%
值),比如 50%
:
.container {grid-template-columns: minmax(100px, 50%) 1fr 1fr;
}
Demo 地址:https://codepen.io/airen/full/QWrXOJm
當minmax(MIN, MAX)
的值為百分比值時(示例中的 MAX=50%
),那么它們就是一個動態值。上一節課程中我們聊到過,網格軌道的值是一個百分比值時:
grid-template-columns
或grid-auto-columns
值是百分比值時,它相對于網格容器內聯軸(inline-size
)尺寸計算;grid-template-rows
或grid-auto-rows
值是百分比值時,它相對于網格容器塊軸(block-size
)尺寸計算。
所以我們示例中的 MAX
值相對于網格容器寬度來計算,即示例中的 MAX
的值等于 W × 50%
(其中 W
是網格容器的寬度):
- 當
W = 1000px
時,MAX = 50% × (1000px - 20px) = 490px
; - 當
W = 800px
時,MAX = 50% × (800px - 20px) = 390px
; - 當
W = 500px
時,MAX = 50% × (500px - 20px) = 240px
; - 當
W = 300px
時,MAX = 50% × (300px - 20px) = 140px
; - 當
W = 200px
時,MAX = 50% × (200px - 20px) = 90px
。
這樣一來,minmax(100px, 50%)
代表的范圍值也會隨著網格容器寬度而變化,并且計算出來的 MAX
值也有可能會比 MIN
值小,比如當你把網格容器寬度調整到 200px
時,計算出來的 MAX
值就要比 MIN
值小。出現這種現象時,MAX
值將會被忽略,minmax(MIN, MAX)
函數最終會取 MIN
值作為函數的返回值。
這個觀點是通用的:
minmax(MIN, MAX)
函數,如果MAX
小于MIN
時,MAX
將會被忽略,最終minmax(MIN, MAX)
函數將會返回MIN
的值 。
minmax(MIN, MAX)
函數中的兩個參數都可以取百分比(%
)值,比如:
.container {grid-template-columns: minmax(30%, 50%) 1fr 1fr;
}
但是這樣使用,有可能計算出來的值會比網格軌道中網格項目內容(元素)的最小尺寸(min-content
)還會小,甚至網格列寬會趨于 0
,將會造成網格項目溢出網格列軌道:
Demo 地址: https://codepen.io/airen/full/BaxXydr
因此,在使用 minmax(MIN, MAX)
函數設置網格軌道時,不建議 MIN
和 MAX
都取 <percentage>
(%
)值,更建議在 minmax(MIN, MAX)
中把 %
值和其他類型值結合起來使用,比如:
/* <inflexible-breadth>, <track-breadth> 值 */
minmax(400px, 50%) /* ~> MIN = 400px; MAX = 50% */
minmax(30%, 300px) /* ~> MIN = 30%; MAX = 300px *//* <fixed-breadth>, <track-breadth> 值 */
minmax(1fr, 50%) /* ~> MIN = 1fr; MAX = 50% */
minmax(400px, 50%) /* ~> MIN = 400px; MAX = 50% */
minmax(30%, 300px) /* ~> MIN = 30%; MAX = 300px */
minmax(50%, min-content) /* ~> MIN = 50%; MAX = min-content */ /* <inflexible-breadth>, <fixed-breadth> 值 */
minmax(400px, 50%) /* ~> MIN = 400px; MAX = 50% */
minmax(30%, 300px) /* ~> MIN = 30%; MAX = 300px */
minmax(min-content, 50%) /* ~> MIN = min-content MAX = 50% */
在 minmax(MIN, MAX)
函數中的參數值還可以是一個 <flex>
值(fr
單位值),如果該函數的有一個值是 fr
單位的值時,它會按 fr
計算來取值,而且和其他設置了 fr
單位值一起計算,分配網格容器可用空間。比如:
.container {grid-template-columns: minmax(100px, 1fr) 1fr 1fr;
}
當 minmax(100px, 1fr)
取 MIN
值時,則返回的是 100px
;當它取 MAX
值時,則返回的是 1fr
,此時 grid-template-columns
的值相當于 1fr 1fr 1fr
。每個 fr
則等于網格容器可用空間的三分之一(因為總共有3
個 fr
,即 1fr + 1fr + 1fr
)。
Demo 地址: https://codepen.io/airen/full/VwxoYor
當你改變網格容器尺寸時,你會發現,如果網格容器有足夠空間,minmax(100px, 1fr)
則會取 1fr
,反之則會取 100px
:
不過需要注意的是,minmax(MIN, MAX)
函數取 fr
單位值時,不能同時給 MIN
和 MAX
都設置 fr
單位的值,因為兩個參數值都取 fr
單位值,瀏覽器會視該屬性值無效:
針對這一點,W3C 規范中有做過相應的描述:
也就是說,在 minmax(MIN, MAX)
函數中使用 fr
單位值時,只能用于 MAX
值中 。換句話說,minmax(MIN, MAX)
中 MAX
取 fr
單位值,可以和其他單位(除 fr
之外)類型 MIN
值混合使用,比如示例中的 minmax(100px, 1fr)
。即使是這樣,也有可能 1fr
計算出來的值會小于100px
,要是出現這種現象,minmax(100px, 1fr)
并不會無效,它最終會返回 MIN
的值(即 100px
)。
在介紹 fr
的時候,我們聊到所有設置 1fr
值的列網格軌道,并不一定能讓所有網格列軌道寬度相等,但可以使用 minmax(0, 1fr)
來替代 1fr
,實現列相等(均分列)的布局效果:
.container {grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr); /* 等同于 */grid-template-columns: repeat(3, minmax(0, 1fr));
}
Demo 地址: https://codepen.io/airen/full/xxjvwqQ
正如你所看到的,設置 minmax(0, 1fr)
的網格軌道的寬度是在 0 ~ 1fr
之間變化,最小為 0
,最大不會超過 1fr
。
當然,你也可以將 MAX
設置為0
,比如 minmax(100px, 0)
、minmax(30%, 0)
都是有效的,只不過最終會取 MIN
的值作為 minmax(MIN, MAX)
函數的返回值,這也符合前面的規則,當 minmax(MIN, MAX)
函數中的 MAX
值小于 MIN
值時,會返回 MIN
值 。
另外需要注意的是,示例中 minmax(0, 1fr)
是一個有效值,但 minmax(1fr, 0)
則是一個無效值。這是因為 minmax(MIN, MAX)
函數只能在 MAX
參數設置 fr
單位值,否則 minmax(MIN, MAX)
也將是一個無效值 。
minmax(MIN, MAX)
函數參數除了可以取長度值之外,還可以取一些描述長度的關鍵詞,比如 auto
、min-content
、max-content
等。比如:
grid-template-columns: minmax(auto, auto) minmax(min-content, min-content) minmax(max-content, max-content);
依舊通過幾個簡單的示例向大家展示 minmax(MIN, MAX)
函數取關鍵詞作為值時,對網格軌道尺寸有何影響。先來看 auto
值:
.container {grid-template-columns: minmax(auto, auto) 1fr 1fr;
}
在 minmax(MIN, MAX)
函數中使用關鍵詞 auto
時:
- 當
auto
作為MAX
值(minmax(100px, auto)
),auto
值相當于max-content
(minmax(100px, max-content)
),即minmax(100px, auto)
等同于minmax(100px, max-content)
。 - 當
auto
作為MIN
值(minmax(auto, 1fr)
),它的值由對應網格軌道中內容的最小尺寸指定,auto
有時相當于min-content
(minmax(min-content, 1fr)
),即minmax(auto, 1fr)
等同于minmax(min-content, 1fr)
,但并非總是如此,因為有時候會受網格項目的min-width
(min-inline-size
)或min-height
(min-block-size
)值影響。如果顯式指定網格項目的min-width
或min-inline-size
,那么min-content
等于min-width
或min-inline-size
。
Demo 地址: https://codepen.io/airen/full/VwxovVY
當 minmax(MIN, MAX)
函數取 min-content
值時,它的大小由相應網格軌道中的內容來決定,在網格列軌道中,min-content
的值將等同于所在列網格軌道中網格項目的內容最小尺寸。
.container {grid-template-columns: minmax(min-content, min-content) 1fr 1fr;
}
示例中的第一列尺寸始終等于所有列的內容最小尺寸:
Demo 地址:https://codepen.io/airen/full/oNdKjKX
min-content
值可以只是 minmax(MIN, MAX)
函數當中的某一個參數值,它可以和其他值類型混合使用,但需要注意:
- 當
minmax(MIN, MAX)
中的MAX
值為min-content
時,如果min-content
計算出來的值小于MIN
,minmax(MIN, MAX)
函數返回的則是MIN
值,反之則返回的是MIN
至min-content
之間的一個范圍值。 - 當
minmax(MIN, MAX)
中的MIN
值為min-content
時,如果min-content
計算出來的值大于MAX
,minmax(MIN, MAX)
函數返回的是min-content
,反之則返回的是min-content
至MAX
之間的一個范圍值。
Demo 地址:https://codepen.io/airen/full/Vwxoejj
minmax(MIN, MAX)
取 max-content
值時有點類似于 min-content
,不同的是取最大長度,這個長度也稱為“理想大小”,它可以容納它包含的內容。比如網格項目是一個句子,那么理想長度就是這個句子長度,而且不用考慮長度,也不會換行:
.container {grid-template-columns: minmax(max-content, max-content) 1fr 1fr;
}
Demo 地址: https://codepen.io/airen/full/rNvXxvN
同樣的,max-content
在 minmax(MIN, MAX)
函數中與其他類型值混合使用,需要注意的是:
- 當
minmax(MIN, MAX)
函數中的MAX
值為max-content
時,如果max-content
的計算值大于MIN
值時,minmax(MIN, MAX)
函數返回的值是一個MIN
至max-content
計算值之間的范圍值;反之會忽略max-content
,函數返回的是MIN
值。 - 當
minmax(MIN, MAX)
函數中的MIN
值為max-content
時,如果max-content
的計算值小于MAX
值時,minmax(MIN, MAX)
函數返回的值是一個max-content
至MAX
之間的范圍值;反之則會返回max-content
。
Demo 地址:https://codepen.io/airen/full/poVMgxm
這樣一來,在 minmax(MIN, MAX)
函數中同時使用 min-content
和 max-content
時,你可以得到一個響應式極強的布局效果,網格項目的內容永遠不會溢出所在網格軌道:
.container {grid-template-columns: minmax(min-content, max-content) 1fr 1fr;
}
Demo 地址:https://codepen.io/airen/full/zYjgrVq
我們來看一個真實的 Web 布局案例。使用 minmax(MIN, MAX)
函數來構建一個博客文章展示的頁面:
實現上圖這種帶有響應式布局效果,僅僅使用幾行 CSS 代碼即可:
<body><main><h2>現代 Web 布局</h2></main>
</body>
body {grid-template-columns: minmax(1rem, 1fr) minmax(auto, 70ch) minmax(1rem, 1fr);
}main {grid-column: 2 / 3;
}
Demo 地址: https://codepen.io/airen/full/BaxXKNv
示例中的 grid-template-columns
創建了一個三列網格布局,其中第一列和第三列的寬度是由 minmax(1rem, 1fr)
決定,當視窗足夠寬時,這兩列的寬度是 1fr
,當視窗沒有足夠寬的時候,這兩列的寬度是 1rem
,所以你會看到,在桌面端上,mian
(主內容)兩側有很大的空白空間,實現了類似主內容水平居中的效果;在平板和手機端上時,兩側只有 1rem
的間距。而主內容(即第二列)列寬是 minmax(auto, 70ch)
,瀏覽器有足夠空間時,它的寬度是 70ch
,如果瀏覽器視窗空間不足時,則由 main
自己的內容來決定(即 auto
)。
網格布局中,使用 repeat()
函數,minmax(MIN, MAX)
和 auto-fit
或 auto-fill
(RAM布局技術 )可以構建一個無媒體查詢的響應式布局。
.container {display: grid;grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));gap: 1.5rem;
}
雖然效果上看上去不錯,但它有一個缺陷存在,當瀏覽器視窗的寬度小于 minmax(MIN, MAX)
中的 MIN
值時,瀏覽器就會出現水平滾動條或溢出內容被裁剪:
Demo 地址: https://codepen.io/airen/full/mdLZMBp
其實,我們可以在 minmax(MIN, MAX)
函數中嵌套 CSS 的比較函數(min()
、max()
、clamp()
),可以讓該布局更為完美。比如,你可以在 minmax(MIN, MAX)
函數中嵌套一個 min()
函數:
.container {display: grid;grid-template-columns: repeat(auto-fill, minmax(min(100%, 250px), 1fr));gap: 1.5rem;
}
在 minmax(MIN, MAX)
函數中嵌套了一個 min(100%, 250px)
函數。在我們這個示例中,min()
函數中的百分比也是相對于網格容器的內聯軸方向尺寸(即寬度)計算,min()
函數中的兩個參數會相互比較,并且取更小的值作為該函數的返回值,它有點類似于設置了一個 max-width
值,即 min(100%, 250px)
相當于 max-width: 250px
。
就該例而言,網格容器的寬度會隨著瀏覽器視窗寬度變小,也就是說,當瀏覽器視窗寬度小到一個臨界值時,min(100%, 250px)
返回的值就會小于 250px
,此時, min(100%, 250px)
函數返回的值就是 100%
,這樣一來,就可以避免瀏覽器出現水平滾動條(除非小到無法容納內容):
Demo 地址:https://codepen.io/airen/full/NWzKwwN
min() 、 max() 和 clamp() 函數
min()
、max()
和clamp()
統稱為 CSS 比較函數,可以給這些函數傳入一個列表值,并對這些值做相應的比較,最終返回一個最適合的值。它們也可以像clac()
函數,支持使用加法 (+
)、減法 (-
)、乘法 (\*
) 和除法 (/
) 的數學表達式作為值 。
CSS 網格布局中,這些函數都可以用于設置網格軌道和網格溝槽的大小。比如:
.container{grid-template-columns: 1fr min(65ch, 100%) 1fr;gap: clamp(1.5rem, 6vmax, 3rem);
}
min()
、max()
和 clamp()
函數用來設置網格軌道尺寸時和 minmax(MIN, MAX)
是有一定差異的:
minmax(MIN, MAX)
返回的是MIN ~ MAX
之間的一個值,最小是MIN
,最大是MAX
;min()
返回的是函數列表參數中最小的值,比如min(100px, 200px)
返回的是100px
;max()
返回的是函數列表參數中最大的值,比如max(100px, 200px)
返回的是200px
;clamp(MIN, VAL, MAX)
更類似于minmax(MIN, MAX)
,返回的是一個區間值。即clamp(MIN, VAL, MAX)
,其中MIN
表示最小值,VAL
表示首選值,MAX
表示最大值。如果VAL
在MIN
和MAX
之間(MIN < VAL < MAX
),則使用VAL
作為函數的返回值;如果VAL
大于MAX
(VAL > MAX
),則使用MAX
作為函數的返回值;如果VAL
小于MIN
(VAL < MIN
),則使用MIN
作為函數的返回值。
我們來看一個 CSS 比較函數用于網格軌道尺寸設置的示例。
Demo 地址: https://codepen.io/airen/full/BaxXKNv
這個示例是前面介紹 minmax(MIN, MAX)
函數的示例:
body {display: grid;grid-template-columns: minmax(1rem, 1fr) minmax(auto, 70ch) minmax(1rem, 1fr);
}
使用 minmax(auto, 70ch)
設置了主內容列的尺寸,對于同等效果,我們還可以使用 min()
函數來替換:
body {display: grid;grid-template-columns: minmax(1rem, 1fr) min(100% - 2rem, 70ch) minmax(1rem, 1fr);
}
Demo 地址: https://codepen.io/airen/full/dyKbZLB
兩個方法構建出來的布局效果是等同的。這里使用 min(100% - 2rem, 70ch)
替代了 minmax(auto, 70ch)
,它所起的作用是:
- 當瀏覽器視窗寬度足夠大時(寬屏),
100% - 2rem
計算出值會大于70ch
,因此min(100% - 2rem, 70ch)
將返回70ch
; - 當瀏覽器視窗寬度不夠大時(窄屏),
100% - 2rem
計算出值就會小于70ch
,因此min(100% - 2rem, 70ch)
將返回100% - 2rem
。
你可能會好奇,為什么會是 100% - 2rem
呢?這是因為示例中第一列和第三列,它設置值是 minmax(1rem, 1fr)
,輸出的最小值就是 1rem
,而且只有在瀏覽器視窗寬度較小時才會出現。在這種現象下,如果 min()
函數設置 min(100%, 70ch)
, 就會造成瀏覽器出現水平滾動條或內容溢出。為了避免該現象,把 min()
函數中的 100%
替換成 100% - 2rem
。
你還可以使用 clamp()
函數來替代上面示例中的 min()
函數,讓布局在一個尺寸范圍具有一個更好的效果:
- 設置一個最小屏尺寸,比如
320px - 2rem
(瀏覽器視窗最小寬度是320px
); - 設置一個最大屏尺寸,比如
70c
; - 設置一個最佳值,比如
100vw - 2rem
。
body {display: grid;grid-template-columns:minmax(1rem, 1fr) clamp(320px - 2rem, 100vw - 2rem, 70ch) minmax(1rem, 1fr);
}
效果幾乎是一樣的,但瀏覽器視窗小于 320px
時,將會出現水平滾動條:
Demo 地址: https://codepen.io/airen/full/GRGKygZ
在實際使用的時候,更建議將 CSS 的比較函數與 minmax(MIN, MAX)
以及關鍵詞 min-content
或 max-content
結合起來使用。這樣能更好地幫助你構建一個內在 Web 布局。
fit-content() 函數
上圖這樣的兩列布局是一種很常見的布局,即 側邊欄固定尺寸,主內容欄能隨瀏覽器視窗尺寸進動調整 。你可以像下面這樣使用 CSS Grid 來構建:
body {display: grid;gap: 1.5rem;grid-template-columns: 250px 1fr;grid-template-areas: "sidebar main";
}
但是,如果瀏覽器的視窗尺寸較小,有可能因為缺少足夠的空間導致樣式出現問題。為了避免這種情況發生,通常會在 CSS Grid 布局中使用媒體查詢:
body { display: grid; gap: 1.5rem; grid-template-areas: "sidebar" "main";
} @media (min-width: 760px) { body { grid-template-columns: 250px 1fr; grid-template-areas: "sidebar main"; }
}
Demo 地址:https://codepen.io/airen/full/PoaYEpp
示例中的側邊欄(網格第一列)是一個固定尺寸,即 250px
。不過,我們希望它的尺寸能更靈活些:
- 當瀏覽器視窗足夠寬的時候,它的寬度能大一些,比如是
250px
; - 當瀏覽器視窗比較窄時,它的寬度能小一些,比如根據內容來決定。
你可能會想到使用 minmax(min-content, 250px)
來實現。在網格布局中除了使用 minmax(MIN, MAX)
函數之外,還可以使用 fit-content()
函數。你可以給 fit-content()
函數傳一個 <length-percentage>
值,比如上面的示例,可以將 250px
換成 fit-content(250px)
:
body {grid-template-columns: fit-content(250px) 1fr;
}
Demo 地址: https://codepen.io/airen/full/jOKNZXO
示例中的 fit-content(250px)
其實相當于:
fit-content(250px) = min(min(max-content, available-size), max(min-content, 250px))
公式中的 available-size
指的是網格中的可用寬度 。
除此之外,規范中還提供了另一種公式來描述 fit-content()
:
fit-content(<length-percentage>) = max(minimum, min(limit, max-content))
其中:
- ①:
minimum
代表自動最小值(通常但不總是等于min-content
最小值); - ②:
limit
是作為參數傳給fit-content()
參數,即<length-percentage>
,比如示例中的250px
; - ③:
min()
返回limit
和max-content
中更小的值,比如這個示例,min()
返回的是250px
和max-content
更小的一個; - ④:
max()
返回是minimum
和min(limit, max-content)
兩個值中更大的一個。
如果上面的描述不易于理解的話,我們可以這樣來理解。比如示例中的 fit-content(250px)
,表示該列網格軌道寬度不會超過 250px
,并且可以在內容很短的情況下縮小到 250px
以下。
另外,一般使用 fit-content()
函數時,傳遞給該函數的參數 <length-percentage>
一般都是小于元素的 max-content
值。比如 fit-content(250px)
,其中 250px
是小于元素 max-content
值。
在這種情況下,元素最終的渲染尺寸介于 min-content
和 250px
之間。具體是多少,還要看當前的可用空間尺寸(available-size
),如果可用空間充足,會使用最大的 250px
,如果可用空間小,就會取 min-content
到 250px
之間的某個值,如果可用空間不充足(比 min-content
還小),那就會使用最小值 min-content
,不能再小。
當然,如果 fit-content()
函數中的值比元素的 max-content
還大,那么元素最終渲染尺寸介于 min-content
和 max-content
之間。具體是多少還要看當前的可用空間大小,與上面類似。
總之,fit-content()
返回的最小值是 min-content
,不能比 min-content
值更小。
需要注意的是,使用 fit-content()
函數設置網格軌道時,該函數只能接受一個 <length-percentage>
(即 <length>
值,比如 250px
;或 <percentage>
值,比如 25%
)。其他值用于該函數將會被視為一個無效值,比如一個 <flex>
類型的值 1fr
。
小結
在這一節課程中,主要和大家一起探討了可用于網格軌道尺寸設置的 CSS 函數,其中 repeat()
、minmax()
和 fit-content()
函數是 CSS 網格布局中獨有的,而 min()
、max()
和 clamp()
函數(CSS 比較函數 )類似于 CSS 的 calc()
函數,除了可以用來設置網格軌道尺寸之外,還可以用于其他長度屬性上,比如 padding
、margin
、width
和 font-size
等。
除了 repeat()
函數之外,其他函數都可以用于 grid-template-columns
、grid-template-rows
、grid-auto-columns
和 grid-auto-rows
屬性上。 repeat()
函數只用于 grid-template-columns
和 grid-template-rows
屬性上設置顯式網格軌道尺寸。
在 CSS 網格布局中,我們應該盡可能避免使用像 <length>
和 <percentage>
這樣的長度值來設置網格軌道尺寸,盡可能地使用這些函數與關鍵詞 min-content
、max-content
、auto
和 <flex>
(fr
)值來設置網格軌道大小,這樣做可能讓你的網格更靈活,不容易造成內容溢出打破 Web 布局!