數學不會背叛你,數學不會就是不會。
我現在寫三角函數都是去 Google 的,不要問。注意!本篇可能會出現大量的加減乘除,如有出現頭暈目眩、噁心想吐、手腳冰冷無力等狀況,請立即關閉本篇文章,閃光洽關心您的身體健康。
或者你要去驗孕也是可以的。
容器計算
前幾篇有提到剩餘空間、元件填充等等,我們現在就來看一下實際上 Flex 容器是怎麼做運算的。首先是 flex-grow
的計算方式,在數值總和大於 1 的一般情況下來看,但且記得,必須容器上有存在 剩餘空間 的情況下才能被分配。
所以我們來描述一下我們的容器跟元件,
- Flex 容器為
600px
主要軸尺寸。 - Flex 元件 1 為寬度
200px
,flex-grow
設定為1
。 - Flex 元件 2 為寬度
100px
,flex-grow
設定為2
。
首先,我們可以得知剩餘空間為,
剩餘空間 = 600px - (200px + 100px) = 300px
然後我們就能依照 flex-grow
來分配,
Flex 元件 1 擴充寬度 = 300px x 1 / (1 + 2) = 100px
Flex 元件 2 擴充寬度 = 300px x 2 / (1 + 2) = 200px
所以畫面最後會得到,
Flex 元件 1 寬度 = 200px + 100px = 300px
Flex 元件 2 寬度 = 100px + 200px = 300px
看起來是不是沒有很難,只要會加減乘除就好了。然後,我在一開始有提到 flex-grow
總和小於 1
的情況,
- Flex 容器為
600px
主要軸尺寸。 - Flex 元件 1 為寬度
200px
,flex-grow
設定為0.1
。 - Flex 元件 2 為寬度
100px
,flex-grow
設定為0.2
。
剩餘空間就不贅述了,但是分配的方式不太一樣。
Flex 元件 1 擴充寬度 = 300px x .1 / 1 = 30px
Flex 元件 2 擴充寬度 = 300px x .2 / 1 = 60px
所以你會發現最終我們得到的總寬度是,
Flex 元件 1 寬度 = 200px + 30px = 230px
Flex 元件 2 寬度 = 100px + 60px = 160px
這也是為何容器雖然有 flex-grow
但是沒有被填滿的情況。至於 flex-shrink
的部分基本上也是雷同的,只是從擴充變成壓縮方向相反了而已。
尺寸干擾
對於 flex-grow
, flex-shrink
這兩件事情,被干擾的機率其實頗高,
min-width
,max-width
min-content
,max-content
max()
,min()
如果不確定自己在做什麼,盡量不要在使用 flex-grow
或 flex-shrink
的情況下,使用這些設定、函數或關鍵字,還要預期會得到相對應的尺寸。
基本上在彈性設計結構的狀況下,Flex 元件尺寸應該是提供一種 確保 空間使用的狀況符合預期,請盡量不要把 Flex 元件尺寸當作是期待值。如果你要這麼做,請確保你使用的是 flex: 0 0 auto
,並且保證你的元件都有相對應尺寸。
至於 minmax()
,在 w3c 有特別說明 minmax()
這個 CSS Function 是給 Grid Layout 使用,所以你在 Flexbox 元件上並無法使用這個函示來計算尺寸。
回到一開始我們提到的 flex-basis
的相關描述,我們再來補充一點看起來比較 沒用 少用的寫法,這裡的重點,
請留意
width
何處會失效
設定 | 寬度有效值 |
---|---|
flex-basis: max(10vw, 50rem); |
計算數值,取當下最大值 |
flex-basis: max(10vw, 50rem); width: 60px |
計算數值,取當下最大值 |
flex-basis: min(10vw, 50rem); width: 60px |
計算數值,取當下最小值 |
flex-basis: min(10vw, 50rem); width: content |
計算數值,取當下最小值 |
flex-basis: auto; width: min(10vw, 50rem); |
計算數值,取當下最小值 |
flex-basis: auto; width: content; |
計算數值,取容器內容尺寸 |
flex-basis: min(10vw, 50rem); max-width: 80px; |
80px |
flex-basis: min(10vw, 50rem); min-width: 100rem; |
100rem |
總結來說,除了 max-width
, min-width
會強迫覆寫 flex-basis
以外,扣除 auto
會被 width
覆寫,其餘的設定都還是以 flex-basis
為準。換句話說,如果你使用以下的寫法,就會遇到一些很神奇的事情,
.flex-container {
width: 600px;
}
.flex-item {
flex: 0 0 content;
width: 200px;
}
如果我們所使用的 HTML 結構像是這樣,
<div class="flex-container">
<div class="flex-item">1</div>
<div class="flex-item">2</div>
<div class="flex-item">3</div>
<div class="flex-item">4</div>
</div>
根據簡單的數學,我們有 4 個容器元件,每個容器元件定義了 width: 200px
,所以我們理論上會得到,4 x 200px = 800px
總共是 800px
的元件尺寸。然而,當我們的元件容器使用 flex: 0 0 content
時,我們的想像是,
- 不會填充
- 不會壓縮
- 依照元件內容設定尺寸
還記得我們 第一篇 寫了這個結果嗎?
設定 | 寬度有效值 |
---|---|
flex-basis: content; width: 60px; |
60px |
所以真的嗎?我們來看看實際結果,
驚不驚喜?意不意外?
然而,當我們把他分開來寫的時候,
.flex-item {
flex-grow: 0;
flex-shrink: 0;
flex-basis: content;
width: 200px;
}
在這個時候他會恢復正常,這才是我們覺得不被壓縮的樣子。
為什麼?
官方有這樣的說明,
A unitless zero that is not already preceded by two flex factors must be interpreted as a flex factor. To avoid misinterpretation or invalid declarations, authors must specify a zero <‘flex-basis’> component with a unit or precede it by two flex factors.
因為 flex: 0 0 content
這樣的設定,是兩個 0 的無單位因子,再加上 content
這種無單位 flex-basis
設定,所以會造成混淆。他會被當作單一彈性因子來看待,也就是等同於只設定了 flex-grow
跟 flex-basis
兩組設定而已。
為何只有這兩組?請看原始 flex
的設定值,
none | [ <‘flex-grow’> <‘flex-shrink’>? || <‘flex-basis’> ]
這樣可以理解為何會出錯了吧。由於 flex-shrink
預設為 1
,所以當我們照剛剛的寫法來做的時候,就會被轉成 flex: 0 1 content
這樣的結果,並不會是你所設定的 flex: 0 0 content
, 後者的設定是不太合法 的寫法。
不能說寫錯,而是在理解上官方的規定如此。
auto
不在此限。
零尺寸元件
另外一點,雖然你可以定義 Flex 元件尺寸,但不代表這個元件尺寸不會發生零尺寸(zero-sized)的情況。根據 Flex 容器的特性,這些所謂的零尺寸元件,會盡可能的被放在同一行,也就是說,在多行的情況下,即便第一行最後一個元件剛好填滿容器,在下一行開始之前的零尺寸元件,都會被放在上一行裡面。
其實零尺寸元件除了是空白元件外,也可能是寬度設定為 0
的元件。
關於 gap
在 CSS Box Alignment Module Level 3 當中,已經提供了 gap
的樣式可以使用,目前的支援度來說也算不錯,
前幾篇提到了 gap
這件事情,有這個樣式就能解決使用 padding
或 margin
的寬度問題。
.flex-container {
display: flex;
gap: 10px;
}
請留意,gap
還是有分軸方向,所以會有以下幾種寫法,
column-gap
交叉軸方向的間隔。row-gap
主要軸方向的間隔。gap
兩個軸方向的間隔,等於上述兩個縮寫。
但是,這不是沒有後遺症的。
<div class="flex-container">
<div class="flex-item"></div>
<div class="flex-item"></div>
<div class="flex-item"></div>
<div class="flex-item"></div>
</div>
我們在這種結構下使用 gap
的設定,
.flex-container {
display: flex;
width: 800px;
gap: 10px;
}
.flex-item {
flex: 0 0 auto;
width: 200px;
}
最終會造成什麼結果呢?
請注意,如果你的元件 flex-shrink
設定為 0
。那麼,gap
的空間就會將 Flex 容器撐開,也就是,當你的 Flex 容器使用 overflow: hidden
的話,最後一個 Flex 元件會被切斷(因為超出了原本寬度設定了)。
而,若你將 flex-shrink
設定為非零值,那麼,你的 Flex 容器尺寸會生效,gap
的尺寸加上 Flex 元件尺寸,會視為 溢出尺寸 來處理,只是,gap
尺寸是 永遠不會被壓縮,所以倒楣的就是 Flex 元件。
小記
Flexbox 其實也沒有很複雜,最麻煩的其實還是尺寸處理。剩下的就交給上天安排就可以了,如果發現不對勁可以擲茭問一下媽祖也是可以的。
Flexbox 告一段落,明天會開始講 Grid 的部分。其實無論是 Flex 還是 Grid,多半都會牽扯到很多其他的模組,不過全部拉進來講會過於離題,所以 Flex 的部分就到此為止。
Flexbox 小遊戲,有興趣的可以玩玩看。
https://flexboxfroggy.com
Chrome Flexbox bug list
https://bugs.chromium.org/p/chromium/issues/list?q=component:Blink>Layout>Flexbox
Firefox Flexbox bug list
https://bugzilla.mozilla.org/buglist.cgi?quicksearch=flexbox
Safari Flexbox bug list
https://bugs.webkit.org/buglist.cgi?quicksearch=flexbox
目錄與小節:
[CSS] Flex/Grid Layout Modules, part 0