Flexbox 佈局中不爲人知的細節
Flexbox 佈局 已是目前最爲流行的 Web 佈局方式之一,它給 Web 開發者在完成頁面或組件的 UI 佈局帶來了極大的靈活性和便利性。但也是因爲它有極大的靈活性,裏面隱藏了一些不爲人知的細節,如果不是對 Flexbox 極爲熟悉或者對其規範極爲了解的話,其中有很多細節將會被遺漏,而這些細節又會讓你在使用的感到困惑,甚至是帶來一定的麻煩。
這次在優化 imgcook 的 Flexbox 佈局時,重新閱讀了一次 Flexbox 的規範,發現自己曾忽略了部分重要信息。爲此在這篇文章中,將 Flexbox 佈局,CSS 的書寫模式,邏輯屬性,對齊方式結合在一起整理了一篇筆記,希望對於想了解或使用 Flexbox 碰到痛楚的同學有所幫助。
一些術語和概念
Flexbox 術語
術語的統一有助於我們後面更好的討論和解決問題。用下圖來描述 Flexbox 中的術語:
主軸和側軸只有在 Flexbox 佈局體系中才有這樣的概念,並不是水平方向永遠都是主軸(Main Axis),垂直方向永遠是側軸(Cross Axis)。主軸和側軸除了會受 Flexbox 中的
flex-direction
取值的影響之外,還會受 CSS 的書寫模式writing-mode
和direction
以及 HTML 的dir
屬性的影響!
塊軸和內聯軸
CSS Box Alignment Module Level 3 引入了兩個新的概念,即 塊軸(Block Axis)和 內聯軸(Inline Axis) :
- 塊軸 是沿 塊 (block) (比如段落元素)的佈局方向延伸的軸, 它會垂直穿過行內軸
- 內聯軸 是在使用特定寫作模式中,沿句子單詞的流動方向的軸。比如對於英語或者中文來說, 內聯軸是水平的
同時 塊軸(Block Axis)又常稱爲列(Column), 內聯軸(Inline Axis)又常稱爲行(Row):
雖然目前爲止在 Flexbox 規範和 Grid 規範中都有自身關於對齊方式的描述,但 CSS 中有關於對齊方式都將收口到 Box Alignment 模塊中;爲此後面對說軸的說法更多的是 “ 塊軸” 和 “ 行內軸”,結合到 Flexbox 佈局中,軸的對應關係是:
- 行內軸(Inline Axis)也對標 Flexbox 中的主軸 Main Axis
- 塊軸(Block Axis)也對標 Flexbox 中的側軸 Cross Axis
塊軸和行內軸同樣受 CSS 的書寫模式
writing-mode
和direction
以及 HTML 的dir
屬性影響。只不過,在 Flexbox 佈局中,還受flex-direction
屬性的影響。
書寫模式
CSS Writing Modes Level 3 規範中的 writing-mode
和 direction
以及 HTML 中的 dir
屬性對於 Flexbox 中的主軸和側軸都會有影響,將會改變主軸和側軸的方向。
邏輯屬性
塊軸 、 內聯軸 、 書寫模式 的出現之後,就有了 塊起點(Block Start)、塊終點(Block End)、內聯起點(Inline Start)和 內聯終點(Inline End):
如果放到 Flexbox 佈局中:
- 內聯起點(Inline Start) 等同於 Flexbox 佈局中的 主軸起點(Main Start)
- 內聯終點(Inline End) 等同於 Flexbox 佈局中的 主軸終點(Main End)
- 塊起點(Block Start) 等同於 Flexbox 佈局中的 側軸起點(Cross Start)
- 塊終點(Block End) 等同於 Flexbox 佈局中的 側軸終點(Cross End)
同時 CSS Logical Properties and Values Level 1 規範中引入了 block-start
、 block-end
、 inline-start
和 inline-end
, 但它們和 Flexbox 中,Grid 中以入 Box Alignment 中的 flex-start
、 start
、 flex-end
和 end
是不等同的,也不是同一領域中的概念。這幾個屬性對應的是物理屬性中的 top
、 right
、 bottom
和 left
:
也就是說,引入 CSS 邏輯屬性之後,CSS 盒模型將分 物理盒模型 和 邏輯盒模型 :
CSS 屬性也從此之後有 邏輯屬性 和 物理屬性 之分:
注意,CSS 邏輯屬性也受 CSS 書寫模式 writing-mode
、 directioin
屬性和 HTML 的 dir
屬性影響,而且不同組合之下也不同:
剩餘空間(可用空間)和 不足空間
在 Flexbox 佈局模塊中,Flex 容器中可能會包含一個或多個 Flex 項目。而 Flex 容器和 Flex 項目都有其自身的尺寸大小,那麼就會有 Flex 項目尺寸大小之和大於或小於 Flex 容器的情景:
- 當所有 Flex 項目尺寸大小之和小於 Flex 容器時,Flex 容器就會有多餘的空間沒有被填充,那麼這個空間就被稱爲 Flex 容器的剩餘空間(Positive Free Space)
- 當所有 Flex 項目尺寸大小之和大於 Flex 容器時,Flex 容器就沒有足夠的空間容納所有 Flex 項目(Flex 項目會溢出 Flex 容器),那麼多出來的這個空間就被稱爲不足空間(Negative Free Space),也被稱爲負空間
Flex 容器 和 Flex 項目
在元素上使用 display
設置值爲 flex
或 inline-flex
,該容器會成爲 Flex 容器,該容器下的子元素,包括 文本節點,僞元素。
使用 flex
和 inline-flex
的具體場景:
如果元素顯式設置了 display
的值爲 flex
或 inline-flex
,Flex 項目在未顯式設置與尺寸大小有關的屬性時,Flex 項目都將會按其內容大小來計算自身大小。
- 設置爲
display: flex
時,Flex 容器未顯式設置與寬度相關的屬性時,其寬度與其父容器等同(相當於width: 100%
) - 設置爲
display: inline-flex
時,Flex 容器未顯式設置與寬度相關的屬性時,其寬度等同於所有 Flex 項目的寬度和
當 Flex 容器中所有 Flex 項目所有寬和大於 Flex 容器時:
- 設置爲
display: flex
時,Flex 項目會溢出 Flex 容器 - 設置爲
display: inline-flex
時,Flex 項目會撐大 Flex 容器,有可能造成 Flex 容器溢出其父元素(或祖先元素)
使用 display: inline-flex
時最好結合 min-width
和 min-height
一起使用。不建議顯式設置 width
和 height
。
display
設置爲 flex
時,Flex 容器從表現形式上類似於塊容器,事實它是一個 Flex 容器,上下文格式是 FFC(Flexbox Formatting Content),因此運用於塊容器(Block Formatting Content)上的一些佈局屬性就不再適用,比如:
- CSS 的
column-*
屬性在 Flex 容器上不起作用 - CSS 的
float
和clear
屬性在 Flex 項目上不起作用,也不會讓 Flex 項目脫離文檔流 - CSS 的
vertical-align
屬性在 Flex 項目上不起作用 - CSS 僞元素
::first-line
和::first-letter
在 Flex 容器上不起作用,而且 Flex 容器不會爲其祖先提供首行或首字母格式化
有一點需要注意, 如果元素的 display
的值爲 inline-flex
,並且該元素顯式的設置了 float
或 position
的值爲 relative
、 absolute
或 fixed
,那麼 display
的計算值是 flex
, 即 Flex 容器表現行爲和 display: flex
等同 。
運用於 Flex 容器上的屬性
指定主軸方向
在 Flex 容器中顯式使用 flex-direction
可以指定主軸方向,如果未顯式設置 flex-direction
屬性,Flex 容器則會採用其默認值 row
。
上圖展示的僅是閱讀方式是 LTR(Left-to-Right),如無特殊聲明,接下來的文檔不會因閱讀方式(即 CSS 的 writing-mode
、 direction
和 HTML 的 dir
屬性)列出不同的示意圖。
除非需要顯式的修改主軸方向,才需要在 Flex 容器上顯式設置 flex-directioin
, 比如像下圖這種排版本方式:
_flex-direction: column
_
row-reverse
和默認值 row
表現恰恰相反,適用於下面這樣佈局場景:
在 Flexbox 佈局中, flex-direction
在指定 Flex 容器主軸方向時,也會對 Flex 項目的排列順序有影響(在不改變 DOM 結構,要實現反方向排版時,非常適合)。除了 flex-direction
可以影響 Flex 項目排列順序之外,在 Flex 項目中顯式使用 order
屬性也可以,並且可以在不影響 DOM 結構,按照你自己任意想要的意圖進行排序。
目前在 imgcook 中使用 Flexbox 佈局時,在 Flex 容器上都會顯式的設置 flex-direction
的值,即使是默認值: row
。在佈局算法優化中,可以做相應的處理,只有在非 row
時纔在 Flex 容器上顯式設置 flex-direction
:
控制 Flex 項目是否換行(Flex 行)
使用 flex-wrap
可以控制 Flex 項目在 Flex 容器換行的方式:
只有所有 Flex 項目寬度總和大於 Flex 容器主軸尺寸時,設置 flex-wrap
屬性才能生效。
flex-wrap
取值爲非 nowrap
(即 wrap
和 wrap-reverse
)都可以讓 Flex 項目換行(列)顯式,其中 wrap-reverse
表現行爲和 wrap
剛好相反。
flex-direction
和 flex-wrap
可以簡寫成 **flex-flow
**。 flex-flow
使用時可以只顯式設置一個值,也可以顯式設置兩個值:
flex-flow
只顯式設置一個值,並且該值和<flex-direction>
相匹配時,flex-wrap
會取值initial
flex-flow
只顯式設置一個值,並且該值和<flex-wrap>
相匹配時,flex-direction
會取值initial
flex-flow
顯式設置兩個值時,flex-direction
和flow-wrap
沒有先後順序之分,即可flex-flow: column wrap
和flex-flow: wrap column
等同
主軸方向對齊方式
在 Flex 容器中使用 justify-content
來控制 Flex 項目在 Flex 容器主軸方向的對齊方式,也可以用來分配 Flex 容器中主軸方向的剩餘空間。使用 justify-content
分配 Flex 容器剩餘空間,主要是將剩餘空間按不同的對齊方式,將剩餘空間分配給 Flex 項目的兩側,即控制 Flex 項目與 Flex 項目之間的間距。
justify-content
存在兩個規範中:
在 Flexbox 佈局模塊中, justify-content
取值只要有以下六種:
需要注意 space-between
、 space-around
和 space-evenly
三者的差異:
space-between
會讓第一個 Flex 項目的盒子起始邊緣與 Flex 容器主軸起點相穩合,最後一個 Flex 項目的盒子結束邊緣與 Flex 容器主軸終點相穩合,其它相鄰 Flex 項目之間間距相等。當 Flex 容器中只有一個 Flex 項目時,其表現行爲和flex-start
等同space-around
會讓第一個 Flex 項目的盒子起始邊緣與 Flex 容器主軸起點間距和最後一個 Flex 項目的盒子結束邊緣與 Flex 容器主軸終點間距相等,並且等於其他相鄰兩個 Flex 項目之間間距的一半。當 Flex 容器中只有一個 Flex 項目時,其表現行爲和center
等同space-evenly
會讓第一個 Flex 項目的盒子起始邊緣與 Flex 容器主軸起點間距和最後一個 Flex 項目的盒子結束邊緣與 Flex 容器主軸終點間距相等,並且等於其他相鄰兩個 Flex 項目之間間距。當 Flex 容器中只有一個 Flex 項目時,其表現行爲和center
等同
如果 Flex 容器沒有額外的剩餘空間,或者說剩餘空間爲負值時, justify-content
的值表現形式:
flex-start
會讓 Flex 項目在 Flex 容器主軸結束點處溢出flex-end
會讓 Flex 項目在 Flex 容器主軸起點處溢出center
會讓 Flex 項目在 Flex 容器兩端溢出space-between
和flex-start
相同space-around
和center
相同space-evenly
和center
相同
在 Flexbox 佈局中,可以使用這些屬性很好控制 Flex 容器的剩餘空間,比如:
側軸方向對齊方式
在 Flexbox 容器中使用 align-items
來控制 Flex 項目在側軸方向的對齊方式。
align-items
的默認值是 stretch
,但只有 Flex 項目示顯式設置 height
(或 width
) 值,Flex 項目纔會被拉伸填滿整個 Flex 容器。
如果 Flex 容器沒有剩餘空間或剩餘空間爲負值是:
flex-start
會讓 Flex 項目在 Flex 容器側軸終點處溢出flex-end
會讓 Flex 項目在 Flex 容器側軸起點處溢出center
會讓 Flex 項目在 Flex 容器側軸兩側溢出baseline
會讓 Flex 項目在 Flex 容器側軸終點溢出,有點類似於flex-start
多行(列)對齊方式
align-content
只適用於 Flex 容器在沒有足夠空間(所有 Flex 項目寬度之和大於 Flex 容器主軸尺寸),並且顯式設置 flex-wrap
的值爲非 wrap
時。
align-content
表現行爲有點類似於 justify-cotent
控制 Flex 項目在主軸方向的對齊方式(分配 Flex 容器主軸剩餘空間),而 align-content
可以用來控制多行狀態下,行在 Flex 容器側軸的對齊方式(分配 Flex 容器側軸剩餘空間)。可以把 align-content
狀態下側軸中的整行當作是 justify-content
狀態下單個 Flex 項目。
align-content
還有一點不同之處,多了一個 stretch
值。當 Flex 容器中所有行的尺寸之和大於 Flex 容器側軸尺寸(Flex 容器側軸沒有可用空間或可用空間爲負值)時,各值表現行爲:
flex-start
會讓 Flex 容器的行在側軸結束點溢出flex-end
會讓 Flex 容器的行在側軸起點溢出center
會讓 Flex 容器行在側軸兩端溢出stretch
表現行爲類似於flex-start
space-around
表現行爲類似於center
space-between
表現行爲類似於flex-start
space-evenly
表現行爲類似於center
間距(行與行,列與列)
gap
用來控制 Flex 項目之間的間距,但會忽略 Flex 項目與 Flex 容器邊緣的間距:
運用於 Flex 項目的屬性
Flex 項目自身對齊方式
在 Flex 容器上可以使用 justify-content
、 align-content
以及 align-items
分配 Flex 容器主軸和側軸的空間(控制 Flex 容器中所有 Flex 項目對齊方式)。如果你需要對 Flex 項目個體對齊方式做處理,可以使用 align-self
:
align-self
取不同值的效果:
Flex 項目的 align-self
顯式設置值爲 auto
時不會覆蓋 Flex 容器的 align-items
; 另外如果在 Flex 項目上顯式設置 margin
的值爲 auto
時,Flex 項目的 align-self
值將會失效。
類似上圖這樣的場景, align-self
就非常實用。
Flex 項目排序
在 Flex 容器中使用 flex-direction
可以對 Flex 容器中的所有 Flex 項目按 “ LTR”、“ RTL”、“ TTB” 或 “ BTT” 方向排列。
- LTR :
flex-driection: row
- RTL :
flex-direction: row-reverse
- TTB :
flex-direction: column
- BTT :
flex-direction: column-reverse
在 Flex 項目上,還可以使用 order
指定具體的數值,在不改變 DOM 結構之下對 Flex 項目進行排序,其中數值越大,越在往後排:
在一些左右,上下互換順序的時候,除了 flex-direction
之外,還可以在 Flex 項目設置 order
:
Flex 項目伸縮計算
Flex 項目中使用 flex
屬性可以根據 Flex 容器的可用空間對自身做伸縮計算,其包含三個子屬性: flex-basis
、 flex-shrink
和 flex-grow
。這幾個屬性都有其初始值:
flex-grow
的初始值爲0
flex-shrink
的初始值爲1
flex-basis
的初始值爲auto
即 flex
的三個子屬性: flex-grow
(擴展比率)、 flex-shrink
(收縮比率)和 flex-basis
(伸縮基準)。這三個屬性可以控制 Flex 項目,具體的表現如下:
flex-grow
:設置 Flex 項目的擴展比率,讓 Flex 項目得到(擴展)多少 Flex 容器剩餘空間(Positive Free Space),即 Flex 項目可能會變大flex-shrink
:設置 Flex 項目收縮比率,讓 Flex 項目減去 Flex 容器不足的空間(Negative Free Space),即 Flex 項目可能會變小flex-basis
:Flex 項目未擴展或收縮之前,它的大小,即指定了 Flex 項目在主軸方向的初始大小
flex
屬性可以指定 1 個值(單值語法) 、 2 個值(雙值語法) 或 3 個值(三值語法) 。
單值語法:值必須爲以下其中之一:
- 一個無單位的數(
<number>
),比如flex: 1
,這個時候它會被當作<flex-grow>
的值 - 一個有效的寬度(
width
)值,比如flex: 30vw
,這個時候它會被當作<flex-basis>
的值 - 關鍵詞
none
、auto
或initial
(即初始值)
雙值語法:第一個值必須爲一個無單位數值,並且它會被當作 <flex-grow>
的值;第二個值必須爲以下之一:
- 一個無單位的數(
<number>
),它會被當作<flex-shrink>
的值 - 一個有效的寬度(
width
)值,它會被當作<flex-basis>
的值
三值語法:
- 第一個值必須是一個無單位數(
<number>
),並且它會被當作<flex-grow>
的值 - 第二個值必須是一個無單位數(
<number>
),並且它會被當作<flex-shrink>
的值 - 第三個值必須爲一個有效的寬度(
width
)值,並且它會被當作<flex-basis>
的值
flex
屬性的取值可以是:
auto
:Flex 項目會根據自身的width
和height
來確定尺寸,但 Flex 項目根據 Flex 容器剩餘空間進行伸縮。其相當於flex: 1 1 auto
initial
:Flex 項目會根據自身的width
和height
來設置尺寸。它會縮短自身以適應 Flex 容器,但不會伸長並吸收 Flex 容器中的額外剩餘空間來適應 Flex 容器。其相當於flex: 0 1 auto
none
:Flex 項目會根據自身的width
和height
來設置尺寸。它是完全非彈性的(既不會縮短,也不會伸長來適應 Flex 容器)。其相當於flex: 0 0 auto
<flex-grow>
:定義 Flex 項目的flex-grow
屬性,取值爲<number>
<flex-shrink>
:定義 Flex 項目的flex-shrink
屬性,取值爲<number>
<flex-basis>
:定義 Flex 項目的flex-basis
屬性。若值爲0
,則必須加上單位,以免被視作伸縮性
flex-grow
計算
flex-grow
計算公式:
示例:
假設 Flex 容器中有四個 Flex 項目,具體參數:
- Flex 容器的寬度是
80vw
- Flex 容器中共有四個 Flex 項目,並且每個 Flex 項目的寬度是
10vw
- Flex 項目寬度總和爲
10vw x 4 = 40vw
- Flex 容器的剩餘空間爲
80vw - 40vw = 40vw
- Flex 項目的
flex-grow
的值分別是0
、1
、2
和3
,所有 Flex 項目的flex-grow
總和爲0 + 1 + 2 + 3 = 6
計算過程:
計算出來的結果:
flex-grow
的取值還可以是 小數值 。如果將上面示例中的 flex-grow
的值分別換成 0
、 0.1
、 0.2
和 0.3
,這個時候 flex-grow
的總和(所有 Flex 項目的 flex-grow
和)就是 0.6
,該值小於 1
。這個時候,Flex 項目同樣會根據 flex-grow
增長因子來瓜分 Flex 容器的剩餘空間,Flex 自身寬度也會變大,但 Flex 容器的剩餘空間不會被全部瓜分完,因爲所有 flex-grow
和小於 1
。就該示例下,只瓜分了 Flex 容器剩餘空間寬度的 60%
。
如果 flex-grow
和小於 1
, 其計算公式如下:
即使 Flex 容器中所有 Flex 項目的 flex-grow
和大於 1
,但也不可以絕對地說,Flex 項目可以根據自身的 flex-grow
所佔比率來瓜分 Flex 容器的剩餘空間。因爲元素的尺寸會受 max-width
的影響。當 Flex 項目顯式設置了 max-width
的值時,當 Flex 項目根據flex-grow
計算出來的寬度大於 max-width
時,Flex 項目會按 max-width
的值爲準。比如我們在前面的示例上,給所有 Flex 項目設置一個 max-width
的值爲 18vw
,此時計算過程和結果如下:
這個時候 Flex 容器剩餘空間並沒有全部用完, 40vw - 0vw - 6.667vw - 8vw - 8vw = 17.333vw
,即 Flex 容器還有 17.333vw
的剩餘空間。
如果 Flex 項目沒有顯式設置與寬度有關的屬性(包括 flex-basis
),那麼 flex-grow
在計算時,Flex 項目會按其內容的寬度來計算。
從上圖可以得到:
- Flex 容器的寬度是
804px
- Flex 項目的寬度分別是
43.36px
、92.09px
、140.83px
和189.56px
,所有 Flex 項目寬度的總和爲465.84px
- Flex 容器的剩餘寬度爲
804px - 465.84px = 338.16px
- 所有 Flex 項目的
flex-grow
值爲1
,即 所有 Flex 項目的flex-grow
總和爲4
將相應的值套用到 flex-grow
的公式中,可以得到:
注意,不同的瀏覽器對小數處理有差異。
flex-shrink
計算
flex-shrink
計算公式:
示例:
假設 Flex 容器有四個 Flex 項目,具體參數如下:
- Flex 容器的寬度是
40vw
- Flex 容器中共有四個 Flex 項目,並且每個 Flex 項目的寬度都是
15vw
- Flex 項目寬度總和爲
15vw x 4 = 60vw
- Flex 容器的不足空間爲
40vw - 60vw = -20vw
- Flex 項目的
flex-shrink
的值分別是0
、1
、2
和3
,所有 Flex 項目的flex-shrink
總和爲0 + 1 + 2 + 3 = 6
計算過程:
計算出來的結果:
flex-shrink
的計算還可以甚至另一個公式來計算:
flex-shrink
和 flex-grow
類似,也可以取小數值。如果 Flex 容器中所有 Flex 項目的 flex-shrink
總和小於 1
,那麼 Flex 容器的不足空間就不會被 Flex 項目按收縮因子瓜分完,Flex 項目會依舊會溢出 Flex 容器。
flex-shrink
總和小於 1
時,其計算公式如下:
基於上面的示例,把 Flex 項目的 flex-shrink
分別設置爲 0
、 0.1
、 0.2
和 0.3
,計算過程如下:
即使 Flex 容器中所有 Flex 項目的 flex-shrink
和大於 1
,但也不可以絕對地說,Flex 項目可以根據自身的 flex-shrink
所佔比率來瓜分 Flex 容器的不足空間。因爲元素的尺寸會受 min-width
的影響。當 Flex 項目顯式設置了 min-width
的值時,當 Flex 項目根據 flex-shrink
計算出來的寬度小於 min-width
時,Flex 項目會按 min-width
的值爲準。比如我們在前面的示例上,給所有 Flex 項目設置一個 min-width
的值爲 10vw
,此時計算過程和結果如下:
在這個情況之下,Flex 項目的最終寬度總和還是會大於 Flex 容器寬度,Flex 項目同樣會溢出 Flex 容器。
flex-shrink
和 flex-grow
還有一點相似,那就是未顯式給 Flex 容器的 Flex 項目顯式設置與寬度有關的屬性時,那麼 Flex 項目的初始寬度會以其內容的寬度作爲基準計算值。
flex-shrink
有一點和 flex-grow
完全不同,如果某個 Flex 項目按照 flex-shrink
計算出來的新寬度趨向於 0
時,Flex 項目將會按照該元素的 min-content
的大小來設置寬度,同時這個寬度將會轉嫁到其他 Flex 項目,再按相應的收縮因子進行收縮。
比如我們將第四個 Flex 項目的 flex-shrink
的值從 3
改爲 9
。根據上面提供的公式,可以獲知,Flex 項目 4 的新寬度等於 15vw - (20vw ÷ 12) × 9 = 0
計算出來的寬度爲 0
,但實際上這個時候渲染出來的寬度是該項目的 min-content
(該示例就是 “shrink” 單詞的寬度,如下圖所示),大約 47.95px
(約 3.66vw
)。那麼這個值將會分成 3
份(因爲該例另外三個 Flex 項目的 flex-shrink
是 0
、 1
和 2
),並且對應的 Flex 項目會繼續分配本應 Flex 項目 4 要收縮的寬度。即:
- Flex 項目 1 新寬度等於
15vw - 20 ÷ 12 × 0 - 3.66 ÷ 3 × 0 = 15vw
(約196.5px
) - Flex 項目 2 新寬度等於
15vw - 20 ÷ 12 × 1 - 3.66 ÷ 3 × 1 = 12.113vw
(約158.6847px
) - Flex 項目 3 新寬度等於
15vw - 20 ÷ 12 × 2 - 3.66 ÷ 3 × 2 = 9.227vw
(約120.869px
)
瀏覽器視窗寬度在 1310px
狀態下渲染出來的結果如下:
在 Flexbox 佈局模塊中,基於前面提到的 Flex 容器的對齊屬性、Flex 項目中的 flex-shrink
和 flex-grow
我們就可以很好的處理 Flex 容器的剩餘空間和不足空間:
- Flex 容器有剩餘空間(所有 Flex 項目的寬度總和小於 Flex 容器的寬度),如果設置了
flex-grow
,Flex 項目會根據擴展因子分配 Flex 容器剩餘空間;在未設置flex-grow
時,在 Flex 容器中是否設置了對齊方式,如果是,那麼會按對齊方式分配 Flex 容器剩餘空間,如果不是,Flex 容器剩餘空間不變 - Flex 容器有不足空間(所有 Flex 項目的寬度總和大於 Flex 容器的寬度),如果設置了
flex-shrink
值爲0
,Flex 項目不會收縮,Flex 項目溢出 Flex 容器;如果未顯式設置flex-shrink
值,Flex 項目分平均分配 Flex 容器不足空間,Flex 項目會變窄(Flex 項目的flex-shrink
的默認值爲1
),如果顯式設置了flex-shrink
的值爲非0
的不同值,那麼 Flex 項目會按照不同的收縮因子分配 Flex 容器不足空間,Flex 項目同樣會變窄
具體的我們可以繪製一張這方面的流程圖:
flex-basis
計算
flex-basis
的計算相對於 flex-grow
和 flex-shrink
更略爲複雜,因爲它和 Flex 項目的 內容(Content) 、** width
**、 **min-width
**和 **max-width
** 都有關係。這裏的關係指的就是**它們之間的權重關係**,簡單地說,**在 Flex 項目中同時出現這幾個屬性時,最終由誰來決定 Flex 項目的寬度**。
在 Flexbox 佈局中,可以使用 flex-basis
來實始化 Flex 項目尺寸,即 在任何 Flex 容器空間(剩餘空間或不足空間)分配發生之前初始化 Flex 項目尺寸。
事實上,在 Flexbox 佈局模塊中 設置 Flex 項目的尺寸大小存在一個隱式的公式:
content
➜width
➜flex-basis
簡單地說,如果 Flex 項目未顯式指定 flex-basis
的值,那麼 flex-basis
將回退到 width
(或 inline-size
)屬性;如果未顯式指定 width
(或 inline-size
)屬性的值,那麼 flex-basis
將回退到基於 Flex 項目內容計算寬度。不過,決定 Flex 項目尺寸大小,還受 flex-grow
和 flex-shrink
以及 Flex 容器大小的影響。而且 Flex 項目 最終尺寸 會受 min-width
、 max-width
(或 min-inline-size
、 max-inline-size
) 屬性限制。這一點必須得注意。
來看一個示例:
1<div>
2 <div></div>
3 <div></div>
4 <div></div>
5 <div></div>
6</div>
7
8
1.flex__container {
2 width: 600px;
3 display: flex;
4
5 border: 1px dashed #f36;
6 align-items: stretch;
7}
8
9
Flex 項目不顯式的設置任何與尺寸大小有關係屬性,即用 content
來撐開 Flex 項目。
1<div>
2 <div>Lorem ipsum dolor sit amet</div>
3 <div>Lorem ipsum dolor sit amet consectetur adipisicing elit</div>
4 <div>Fugiat dolor nihil saepe. Nobis nihil minus similique hic quas mollitia.</div>
5 <div>Lorem ipsum dolor sit amet consectetur adipisicing elit. Molestias consequuntur sequi suscipit iure fuga ea!</div>
6</div>
7
8
在這個示例中,並沒有顯式給 Flex 項目設置 flex-basis
屬性,此時 flex-basis
會取默認值 auto
:
顯式給 Flex 項目設置 width
值。
1:root {
2 --width: 120px;
3}
4
5.flex__item {
6 width: var(--width);
7}
8
9
這個時候所有 Flex 項目寬度都是相等的:
瀏覽器計算出來的 flex-basis
值依舊爲 auto
,但顯式的設置了 width: 120px
,最終 width
屬性的值決定了 Flex 項目的尺寸大小。
顯式給 Flex 項目設置 flex-basis
值,即 Flex 項目同時有 width
和 flex-basis
值。
1:root {
2 --width: 120px;
3 --flexBasis: 150px;
4}
5
6.flex__container {
7 width: 800px;
8}
9
10.flex__item {
11 width: var(--width);
12 flex-basis: var(--flexBasis);
13}
14
15
雖然在 Flex 項目同時顯式設置了 width
和 flex-basis
,但 Flex 項目最終的尺寸大小採用了 flex-basis
的值:
在 Flexbox 佈局模塊中影響 Flex 項目尺寸大小應該根據其隱式公式 (即 content
➜ width
➜ flex-basis
)來進行判斷。如果要顯式給 Flex 項目設置尺寸大小,其最佳方式是 使用 flex-basis
,而不是 width
(或 inline-size
)。
最後還有一點千萬別忘記:
使用 flex-basis
時會受min-width
和 max-width
(或邏輯屬性中min-inline-size
或max-inline-size
)的限制。
在 CSS 中,如果元素同時出現 width
、 min-width
和 max-width
屬性時,其權重計算遵循以下規則:
- 元素的
width
大於max-width
時,元素的width
等於max-width
,即max-width
能覆蓋width
(max-width
勝出) - 元素的
width
小於min-width
時,元素的width
等於min-width
,即min-width
能覆蓋width
(min-width
勝出) - 當
min-width
大於max-width
時,min-width
優先級將高於max-width
(min-width
勝出)
如果 Flex 項目同時出現 width
、 flex-basis
和 min-width
時,具體的運算過程如下:
- 根據法則:
content
➜width
➜flex-basis
,判斷出運用於 Flex 項目的值,即flex-basis
會運用於 Flex 項目 (flex-basis
勝出) - 再根據法則:Flex 項目的
width
小於min-width
時,Flex 項目的width
等於min-width
,即min-width
能覆蓋width
(**min-width
勝出 **)
這樣一來,如果 flex-basis
小於 min-width
時,Flex 項目的寬度會取值 min-width
,即 min-width
覆蓋 flex-basis
(min-width
勝出)。
如果 Flex 項目同時出現 width
、 flex-basis
和 max-width
時,具體的運算過程如下:
- 根據法則:
content
➜width
➜flex-basis
,判斷出運用於 Flex 項目的值,即flex-basis
會運用於 Flex 項目 (flex-basis
勝出) - 再根據法則:Flex 項目的
width
大於max-width
時,Flex 項目的width
等於max-width
,即max-width
能覆蓋width
(max-width
勝出)
這樣一來,如果 flex-basis
大於 max-width
時,Flex 項目的寬度會取值 max-width
,即 max-width
覆蓋 flex-basis
( max-width
勝出)。
如果 Flex 項目同時出現 width
、 flex-basis
、 min-width
和 max-width
時,會在上面的規則上增加新的一條規則來進行判斷:
- 當
min-width
大於max-width
時,min-width
優先級將高於max-width
(min-width
勝出)。
那麼套用到 Flex 項目中:
flex-basis
大於max-width
,Flex 項目的寬度等於max-width
,即max-width
能覆蓋flex-basis
(max-width
勝出)flex-basis
小於min-width
時,Flex 項目的寬度會取值min-width
,即min-width
覆蓋flex-basis
(**min-width
勝出 **)
由於 min-width
大於 max-width
時會取 min-width
,有了這個先取條件我們就可以將 flex-basis
和 min-width
做權重比較,即:** flex-basis
會取 min-width
。反過來,如果 min-width
小於 max-width
時則依舊會取 max-width
,同時要是 flex-basis
大於 max-width
就會取 max-width
**。
如果你理解了的話,可以使用更簡單的規則來決定用於 Flex 項目的尺寸。
首先根據 content
➜ width
➜ flex-basis
來決定用哪個來決定用於 Flex 項目。如果 Flex 項目顯式設置了 flex-basis
屬性,則會忽略 content
和 width
。而且 min-width
是用來設置 Flex 項目的下限值; max-width
是用來設置 Flex 項目的上限值。
用一個簡單的流程圖來描述:
注,Flex 項目上的 flex-shrink
和 flex-grow
也會影響 Flex 項目尺寸大小!
Flex 項目上的 margin
在 Flex 項目顯式設置 margin
的值爲 auto
可以靈活的控制單個 Flex 項目在 Flex 容器中的位置:
比如像下圖這樣的效果,使用 margin-left: auto
就非常的實用:
案例整理
padding 與自動寬問題
針對這個案例, 較好的方案對於內部元素不顯式設置任何關於 padding
和 margin
的屬性。人工實現可能會像下面這樣:
1<div>
2 <span>卷</span>
3 <span></span>
4 <span>¥1000</span>
5</div>
6
7
1.flex__container {
2 display: inline-flex;
3 min-width: 200px;
4 height: 60px;
5
6 border: 1px solid rgba(255, 0, 54, 1);
7 background-color: rgba(255, 0, 54, 0.1);
8 border-radius: 4px;
9 color: #ff0036;
10 font-size: 24px;
11 font-weight: 400;
12}
13
14.flex__container > span {
15 display: inline-flex;
16 justify-content: center;
17 align-items: center;
18}
19
20.divider {
21 border-right: 1px dashed currentColor;
22}
23
24.coupon {
25 min-width: 50px;
26}
27
28.price {
29 flex: 1;
30 min-width: 0;
31 padding: 0 10px;
32}
33
34
- ① 像類似 Button,Badge 等(外形看上去類似於內聯塊),設置 Flex 容器爲
inline-flex
,並且給其設置一個min-width
(默認情況下等同於 Sketch 設計稿)和 一個height
- ② 從設計稿上分析可以得到,前面 “卷” 這個寬度是可知的,在該元素上設置一個固定寬度
width
- ③ 一個約
1px
的分割線,可以使用border
或者定死寬度width
- ④ 最右側 “價格” 是下不可定因素,在 Flex 項目上,可以將其顯式設置
flex: 1
,讓該部分佔用 Flex 容器的剩餘空間 - ⑤ 爲了讓 “價格” 更具有擴展性,當其數值擴展到 Flex 容器無剩餘空間時,數字會緊挨 Flex 容器主軸終點和分割線,爲了讓視覺上更友好,要以在 “價格” 容器設置一個
padding-left
和padding-right
小結
這篇筆記涉及到了 Flexbox 規範中的大部分內容以及一些臨界點,在使用 Flexbox 來完成 UI 上的佈局除了文章中提到的一些基礎內容和細節之外,還有一些其他的東西。比如 Flex 容器中的定位,層級計算等,Flex 容器和 Flex 項目碰到overflow
以及 Flex 容器中的滾動計算等。這些對於場景具有較強的指定性,對於邊界的處理也過於複雜。在我們平常使用 Flexbox 很少甚至不怎麼會碰到。因此沒有在文章中羅列。
如果你在使用 Flexbox,特別是在使用 imgcook 自動還原 UI,效果和你預期不一樣,或者有不合理的地方,都可以隨時來撩偶。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://juejin.cn/post/6940498706143641613?utm_source=gold_browser_extension