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會取值initialflex-flow只顯式設置一個值,並且該值和<flex-wrap>相匹配時,flex-direction會取值initialflex-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-startspace-around表現行爲類似於centerspace-between表現行爲類似於flex-startspace-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的初始值爲0flex-shrink的初始值爲1flex-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 autoinitial:Flex 項目會根據自身的width和height來設置尺寸。它會縮短自身以適應 Flex 容器,但不會伸長並吸收 Flex 容器中的額外剩餘空間來適應 Flex 容器。其相當於flex: 0 1 autonone: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