Flexbox佈局中鮮爲人知的細節

文/ 阿里淘系 F(x) Team - 大漠css

Flexbox佈局 已經是目前最爲流行的Web佈局方式之一,它給Web開發者在完成頁面或組件的UI佈局帶來了極大的靈活性和便利性。但也是由於它有極大的靈活性,裏面隱藏了一些鮮爲人知的細節,若是不是對Flexbox極爲熟悉或者對其規範極爲了解的話,其中有不少細節將會被遺漏,而這些細節又會讓你在使用的感到困惑,甚至是帶來必定的麻煩。html

此次在優化imgcook的Flexbox佈局時,從新閱讀了一次Flexbox的規範,發現本身曾忽略了部分重要信息。爲此在這篇文章中,將Flexbox佈局,CSS的書寫模式,邏輯屬性,對齊方式結合在一塊兒整理了一篇筆記,但願對於想了解或使用Flexbox碰到痛楚的同窗有所幫助。前端

一些術語和概念

Flexbox術語

術語的統一有助於咱們後面更好的討論和解決問題。用下圖來描述Flexbox中的術語:算法


主軸和側軸只有在Flexbox佈局體系中才有這樣的概念,並非水平方向永遠都是主軸(Main Axis),垂直方向永遠是側軸(Cross Axis)。主軸和側軸除了會受Flexbox中的 flex-direction 取值的影響以外,還會受CSS的書寫模式 writing-modedirection 以及HTML的 dir 屬性的影響!瀏覽器

塊軸和內聯軸

CSS Box Alignment Module Level 3 引入了兩個新的概念,即 塊軸(Block Axis) 內聯軸(Inline Axis) :markdown

  • 塊軸 是沿 塊(block) (好比段落元素)的佈局方向延伸的軸, 它會垂直穿過行內軸
  • 內聯軸 是在使用特定寫做模式中,沿句子單詞的流動方向的軸。好比對於英語或者中文來講, 內聯軸是水平的

同時 塊軸(Block Axis)又常稱爲列(Column), 內聯軸(Inline Axis)又常稱爲行(Row):ide


雖然目前爲止在 Flexbox規範和Grid規範中都有自身關於對齊方式的描述,但CSS中有關於對齊方式都將收口到Box Alignment 模塊中;爲此後面對說軸的說法更多的是「 塊軸」 和 「 行內軸」,結合到Flexbox佈局中,軸的對應關係是:oop

  • 行內軸(Inline Axis)也對標 Flexbox中的主軸 Main Axis
  • 塊軸(Block Axis)也對標 Flexbox中的側軸 Cross Axis

塊軸和行內軸一樣受CSS的書寫模式 writing-modedirection 以及HTML的dir 屬性影響。只不過,在Flexbox佈局中,還受 flex-direction 屬性的影響佈局

書寫模式

CSS Writing Modes Level 3 規範中的 writing-modedirection 以及HTML中的 dir 屬性對於Flexbox中的主軸和側軸都會有影響,將會改變主軸和側軸的方向。flex



邏輯屬性

塊軸內聯軸書寫模式 的出現以後,就有了 塊起點(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-startblock-endinline-startinline-end , 但它們和Flexbox中,Grid中以入Box Alignment中的 flex-startstartflex-endend 是不等同的,也不是同一領域中的概念。這幾個屬性對應的是物理屬性中的 toprightbottomleft


也就是說,引入CSS邏輯屬性以後,CSS盒模型將分 物理盒模型邏輯盒模型 :


CSS屬性也今後以後有 邏輯屬性物理屬性 之分:


注意,CSS邏輯屬性也受CSS書寫模式 writing-modedirectioin 屬性和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 設置值爲 flexinline-flex ,該容器會成爲 Flex容器,該容器下的子元素,包括 文本節點,僞元素。


使用 flexinline-flex 的具體場景:


若是元素顯式設置了 display 的值爲 flexinline-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-widthmin-height 一塊兒使用。不建議顯式設置 widthheight

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 ,而且該元素顯式的設置了 floatposition 的值爲 relativeabsolutefixed,那麼 display 的計算值是 flex, 即 Flex容器表現行爲和 display: flex 等同

運用於Flex容器上的屬性


指定主軸方向

在Flex容器中顯式使用 flex-direction  能夠指定主軸方向,若是未顯式設置 flex-direction 屬性,Flex容器則會採用其默認值 row


上圖展現的僅是閱讀方式是 LTR(Left-to-Right),如無特殊聲明,接下來的文檔不會因閱讀方式(即CSS的 writing-modedirection 和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 恰好相反。

組合效果 表現效果
效果1

效果2

效果3

效果4

效果5

效果6

效果7

效果8

flex-directionflex-wrap 能夠簡寫成 **flex-flow **。 flex-flow 使用時能夠只顯式設置一個值,也能夠顯式設置兩個值:

  • flex-flow 只顯式設置一個值,而且該值和 <flex-direction> 相匹配時, flex-wrap 會取值 initial
  • flex-flow 只顯式設置一個值,而且該值和 <flex-wrap> 相匹配時, flex-direction 會取值 initial
  • flex-flow 顯式設置兩個值時, flex-directionflow-wrap 沒有前後順序之分,便可 flex-flow: column wrapflex-flow: wrap column 等同

主軸方向對齊方式

在Flex容器中使用 justify-content 來控制Flex項目在Flex容器主軸方向的對齊方式,也能夠用來分配Flex容器中主軸方向的剩餘空間。使用 justify-content 分配Flex容器剩餘空間,主要是將剩餘空間按不一樣的對齊方式,將剩餘空間分配給Flex項目的兩側,即控制Flex項目與Flex項目之間的間距。

justify-content 存在兩個規範中:

在Flexbox 佈局模塊中, justify-content 取值只要有如下六種:


須要注意 space-betweenspace-aroundspace-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-betweenflex-start 相同
  • space-around  和 center 相同
  • space-evenlycenter 相同

在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-contentalign-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」 方向排列。

  • LTRflex-driection: row
  • RTLflex-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-basisflex-shrinkflex-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> 的值
  • 關鍵詞 noneautoinitial (即初始值)

雙值語法:第一個值必須爲一個無單位數值,而且它會被看成 <flex-grow> 的值;第二個值必須爲如下之一:

  • 一個無單位的數( <number> ),它會被看成 <flex-shrink> 的值
  • 一個有效的寬度( width )值,它會被看成 <flex-basis> 的值

三值語法:

  • 第一個值必須是一個無單位數( <number> ),而且它會被看成 <flex-grow> 的值
  • 第二個值必須是一個無單位數( <number> ),而且它會被看成 <flex-shrink> 的值
  • 第三個值必須爲一個有效的寬度( width )值,而且它會被看成 <flex-basis> 的值

flex 屬性的取值能夠是:

  • auto :Flex項目會根據自身的 widthheight 來肯定尺寸,但Flex項目根據Flex容器剩餘空間進行伸縮。其至關於 flex: 1 1 auto
  • initial :Flex項目會根據自身的 widthheight 來設置尺寸。它會縮短自身以適應Flex容器,但不會伸長並吸取Flex容器中的額外剩餘空間來適應Flex容器。其至關於 flex: 0 1 auto
  • none :Flex項目會根據自身的 widthheight 來設置尺寸。它是徹底非彈性的(既不會縮短,也不會伸長來適應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 的值分別是 0123 ,全部Flex項目的 flex-grow 總和爲 0 + 1 + 2 + 3 = 6
flex-grow 公式中變量名稱 Flex1 Flex2 Flex3 Flex4 總數
Flex項目的 flex-grow 0 1 2 3 0 + 1 + 2+ 3 = 6
Flex項目寬度 10vw 10vw 10vw 10vw 10vw x 4 = 40vw
Flex容器寬度 80vw
Flex容器剩餘空間 80vw - 40vw = 40vw
Flex項目新寬度

計算過程:


計算出來的結果:

flex-grow 公式中變量名稱 Flex1 Flex2 Flex3 Flex4 總數
Flex項目的 flex-grow 0 1 2 3 0 + 1 + 2+ 3 = 6
Flex項目寬度 10vw 10vw 10vw 10vw 10vw x 4 = 40vw
Flex容器寬度 80vw
Flex容器剩餘空間 80vw - 40vw = 40vw
Flex項目新寬度 10vw 16.667vw 23.333vw 30vw

flex-grow 的取值還能夠是 小數值 。若是將上面示例中的 flex-grow 的值分別換成 00.10.20.3 ,這個時候 flex-grow 的總和(全部Flex項目的 flex-grow 和)就是 0.6  ,該值小於 1 。這個時候,Flex項目一樣會根據 flex-grow 增加因子來瓜分Flex容器的剩餘空間,Flex自身寬度也會變大,但Flex容器的剩餘空間不會被所有瓜分完,由於全部 flex-grow 和小於 1 。就該示例下,只瓜分了 Flex容器剩餘空間寬度的 60%

若是 flex-grow 和小於 1 ,其計算公式以下:


flex-grow 公式中變量名稱 Flex1 Flex2 Flex3 Flex4 總數
Flex項目的 flex-grow 0 .1 .2 .3 0 + 1 + 2+ 3 = .6
Flex項目寬度 10vw 10vw 10vw 10vw 10vw x 4 = 40vw
Flex容器寬度 80vw
Flex容器剩餘空間 80vw - 40vw = 40vw
Flex項目新寬度 10vw 14vw 18vw 22vw

即便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-grow 公式中變量名稱 Flex1 Flex2 Flex3 Flex4 總數
Flex項目的 flex-grow 0 1 2 3 0 + 1 + 2+ 3 = 6
Flex項目寬度 10vw 10vw 10vw 10vw 10vw x 4 = 40vw
Flex容器寬度 80vw
Flex容器剩餘空間 80vw - 40vw = 40vw
Flex項目計算出的新寬度 10vw 16.667vw 23.333vw 30vw
Flex項目設置最大寬度 18vw 18vw 18vw 18vw
Flex項目最終寬度 10vw 16.667vw 18vw 18vw

這個時候Flex容器剩餘空間並無所有用完, 40vw - 0vw - 6.667vw - 8vw - 8vw = 17.333vw ,即Flex容器還有 17.333vw 的剩餘空間。

若是Flex項目沒有顯式設置與寬度有關的屬性(包括 flex-basis ),那麼 flex-grow 在計算時,Flex項目會按其內容的寬度來計算。


從上圖能夠獲得:

  • Flex容器的寬度是 804px
  • Flex項目的寬度分別是 43.36px92.09px140.83px189.56px ,全部Flex項目寬度的總和爲 465.84px
  • Flex容器的剩餘寬度爲 804px - 465.84px = 338.16px
  • 全部Flex項目的 flex-grow 值爲 1 ,即 全部Flex項目的 flex-grow 總和爲 4

將相應的值套用到 flex-grow 的公式中,能夠獲得:

flex-grow 公式中變量名稱 Flex1 Flex2 Flex3 Flex4 總數
Flex項目的 flex-grow 1 1 1 1 1 x 4 = 4
Flex項目寬度 43.36px 92.09px 148.83px 189.56px 465.84px
Flex容器寬度 804px
Flex容器剩餘空間 338.16px
Flex項目新寬度 127.9px 176.63px 225.37px 274.1px

注意,不一樣的瀏覽器對小數處理有差別。

flex-shrink 計算

flex-shrink 計算公式:


示例:

假設Flex容器有四個Flex項目,具體參數以下:

  • Flex容器的寬度是 40vw
  • Flex容器中共有四個Flex項目,而且每一個Flex項目的寬度都是 15vw
  • Flex項目寬度總和爲 15vw x 4 = 60vw
  • Flex容器的不足空間爲 40vw - 60vw = -20vw
  • Flex項目的 flex-shrink 的值分別是 0123 ,全部Flex項目的 flex-shrink 總和爲 0 + 1 + 2 + 3 = 6
flex-shrink 公式中變量名稱 Flex1 Flex2 Flex3 Flex4 總數
Flex項目的 flex-shrink 0 1 2 3 0 + 1 + 2+ 3 = 6
Flex項目寬度 15vw 15vw 15vw 15vw 15vw x 4 = 60vw
Flex容器寬度 40vw
Flex容器不足空間 60vw - 40vw = 20vw
Flex項目新寬度

計算過程:


計算出來的結果:

flex-shrink 公式中變量名稱 Flex1 Flex2 Flex3 Flex4 總數
Flex項目的 flex-shrink 0 1 2 3 0 + 1 + 2+ 3 = 6
Flex項目寬度 15vw 15vw 15vw 15vw 15vw x 4 = 60vw
Flex容器寬度 40vw
Flex容器不足空間 60vw - 40vw = 20vw
Flex項目收縮比例 0 0.1667 0.333 0.5
Flex項目新寬度 15vw 11.67vw 8.33vw 5vw

flex-shrink 的計算還能夠甚至另外一個公式來計算:


flex-shrinkflex-grow 相似,也能夠取小數值。若是Flex容器中全部Flex項目的 flex-shrink 總和小於 1 ,那麼Flex容器的不足空間就不會被Flex項目按收縮因子瓜分完,Flex項目會依舊會溢出Flex容器。

flex-shrink 總和小於 1 時,其計算公式以下:


基於上面的示例,把Flex項目的 flex-shrink 分別設置爲 00.10.20.3 ,計算過程以下:

flex-shrink 公式中變量名稱 Flex1 Flex2 Flex3 Flex4 總數
Flex項目的 flex-shrink 0 0.1 0.2 0.3 0.6
Flex項目寬度 15vw 15vw 15vw 15vw 15vw x 4 = 60vw
Flex容器寬度 40vw
Flex容器不足空間 60vw - 40vw = 20vw
Flex項目新寬度 15vw 13vw 11vw 9vw

即便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-shrink 公式中變量名稱 Flex1 Flex2 Flex3 Flex4 總數
Flex項目的 flex-shrink 0 1 2 3 0 + 1 + 2+ 3 = 6
Flex項目寬度 15vw 15vw 15vw 15vw 15vw x 4 = 60vw
Flex容器寬度 40vw
Flex容器不足空間 60vw - 40vw = 20vw
Flex項目收縮比例 0 0.1667 0.333 0.5
Flex項目設置最小寬度 10vw 10vw 10vw 10vw
Flex項目計算出的新寬度 15vw 11.67vw 8.33vw 5vw
Flex項目最終寬度 15vw 11.67vw 10vw 10vw

在這個狀況之下,Flex項目的最終寬度總和仍是會大於Flex容器寬度,Flex項目一樣會溢出Flex容器。

flex-shrinkflex-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-shrink012 ),而且對應的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-shrinkflex-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-growflex-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-growflex-shrink 以及Flex容器大小的影響。並且Flex項目 最終尺寸 會受 min-widthmax-width(或 min-inline-sizemax-inline-size )屬性限制。這一點必須得注意。

來看一個示例:

<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>
複製代碼
.flex__container {
    width: 600px;
    display: flex;

    border: 1px dashed #f36;
    align-items: stretch;
}
複製代碼

Flex項目不顯式的設置任何與尺寸大小有關係屬性,即用 content來撐開Flex項目

<div class="flex__container">
    <div class="flex__item">Lorem ipsum dolor sit amet</div>
    <div class="flex__item">Lorem ipsum dolor sit amet consectetur adipisicing elit</div>
    <div class="flex__item">Fugiat dolor nihil saepe. Nobis nihil minus similique hic quas mollitia.</div>
    <div class="flex__item">Lorem ipsum dolor sit amet consectetur adipisicing elit. Molestias consequuntur sequi suscipit iure fuga ea!</div>
</div>
複製代碼

在這個示例中,並無顯式給Flex項目設置 flex-basis 屬性,此時 flex-basis 會取默認值 auto


顯式給Flex項目設置 width 值。

:root { 
  --width: 120px; 
} 

.flex__item { 
  width: var(--width); 
}
複製代碼

這個時候全部Flex項目寬度都是相等的:


瀏覽器計算出來的 flex-basis 值依舊爲 auto ,但顯式的設置了 width: 120px ,最終 width 屬性的值決定了Flex項目的尺寸大小。

顯式給Flex項目設置 flex-basis 值,即Flex項目同時有 widthflex-basis

:root {
    --width: 120px;
    --flexBasis: 150px;
}

.flex__container {
    width: 800px;
}

.flex__item {
    width: var(--width);
    flex-basis: var(--flexBasis);
}
複製代碼

雖然在Flex項目同時顯式設置了 widthflex-basis ,但Flex項目最終的尺寸大小採用了 flex-basis 的值:


在Flexbox佈局模塊中影響Flex項目尺寸大小應該根據其隱式公式(即 content  ➜ width  ➜ flex-basis  )來進行判斷。若是要顯式給Flex項目設置尺寸大小,其最佳方式是 使用 flex-basis ,而不是 width (或 inline-size )。

最後還有一點千萬別忘記:

使用 flex-basis 時會受min-widthmax-width(或邏輯屬性中min-inline-sizemax-inline-size )的限制

在CSS中,若是元素同時出現 widthmin-widthmax-width 屬性時,其權重計算遵循如下規則:

  • 元素的 width 大於 max-width 時,元素的 width 等於 max-width ,即 max-width 能覆蓋 widthmax-width 勝出
  • 元素的 width 小於 min-width 時,元素的 width 等於 min-width ,即 min-width 能覆蓋 widthmin-width 勝出
  • min-width 大於 max-width 時, min-width 優先級將高於 max-widthmin-width 勝出

若是Flex項目同時出現 widthflex-basismin-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-basismin-width勝出)

若是Flex項目同時出現 widthflex-basismax-width 時,具體的運算過程以下:

  • 根據法則: content  ➜ width  ➜ flex-basis ,判斷出運用於Flex項目的值,即 flex-basis 會運用於Flex項目( flex-basis 勝出)
  • 再根據法則:Flex項目的 width  大於 max-width 時,Flex項目的 width  等於 max-width ,即 max-width 能覆蓋 widthmax-width 勝出

這樣一來,若是 flex-basis 大於 max-width 時,Flex項目的寬度會取值 max-width ,即 max-width 覆蓋 flex-basismax-width 勝出)

若是Flex項目同時出現 widthflex-basismin-widthmax-width 時,會在上面的規則上增長新的一條規則來進行判斷:

  • min-width  大於 max-width  時, min-width 優先級將高於 max-width ( min-width勝出)

那麼套用到Flex項目中:

  • flex-basis 大於 max-width ,Flex項目的寬度等於 max-width ,即 max-width 能覆蓋 flex-basismax-width 勝出
  • flex-basis 小於 min-width 時,Flex項目的寬度會取值 min-width ,即 min-width 覆蓋 flex-basis (** min-width 勝出**)

因爲 min-width 大於 max-width 時會取 min-width ,有了這個先取條件咱們就能夠將 flex-basismin-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 屬性,則會忽略 contentwidth 。並且 min-width 是用來設置Flex項目的下限值; max-width 是用來設置Flex項目的上限值。

用一個簡單的流程圖來描述:


注,Flex項目上的 flex-shrinkflex-grow 也會影響Flex項目尺寸大小

Flex項目上的 margin

在Flex項目顯式設置 margin 的值爲 auto 能夠靈活的控制單個Flex項目在Flex容器中的位置:


好比像下圖這樣的效果,使用 margin-left: auto 就很是的實用:


案例整理

padding 與自動寬問題


針對這個案例,較好的方案對於內部元素不顯式設置任何關於 paddingmargin 的屬性。人工實現可能會像下面這樣:

<div class="flex__container">
  <span class="coupon"></span>
  <span class="divider"></span>
  <span class="price">&yen;1000</span>
</div>
複製代碼
.flex__container {
  display: inline-flex;
  min-width: 200px;
  height: 60px;
  
  border: 1px solid rgba(255, 0, 54, 1);
  background-color: rgba(255, 0, 54, 0.1);
  border-radius: 4px;
  color: #ff0036;
  font-size: 24px;
  font-weight: 400;
}

.flex__container > span {
  display: inline-flex;
  justify-content: center;
  align-items: center;
}

.divider {
  border-right: 1px dashed currentColor;
}

.coupon {
  min-width: 50px;
}

.price {
  flex: 1;
  min-width: 0;
  padding: 0 10px;
}
複製代碼

  • ① 像相似Button,Badge等(外形看上去相似於內聯塊),設置Flex容器爲 inline-flex ,而且給其設置一個 min-width (默認狀況下等同於Sketch設計稿)和 一個 height
  • ② 從設計稿上分析能夠獲得,前面「卷」這個寬度是可知的,在該元素上設置一個固定寬度 width
  • ③ 一個約 1px 的分割線,可使用 border 或者定死寬度 width
  • ④ 最右側「價格」是下不可定因素,在Flex項目上,能夠將其顯式設置 flex: 1 ,讓該部分佔用Flex容器的剩餘空間
  • ⑤ 爲了讓「價格」更具備擴展性,當其數值擴展到Flex容器無剩餘空間時,數字會緊挨Flex容器主軸終點和分割線,爲了讓視覺上更友好,要以在「價格」容器設置一個 padding-leftpadding-right

小結

這篇筆記涉及到了Flexbox規範中的大部份內容以及一些臨界點,在使用Flexbox來完成UI上的佈局除了文章中提到的一些基礎內容和細節以外,還有一些其餘的東西。好比Flex容器中的定位,層級計算等,Flex容器和Flex項目碰到overflow以及Flex容器中的滾動計算等。這些對於場景具備較強的指定性,對於邊界的處理也過於複雜。在咱們日常使用Flexbox不多甚至不怎麼會碰到。所以沒有在文章中羅列。

若是你在使用Flexbox,特別是在使用imgcook自動還原UI,效果和你預期不同,或者有不合理的地方,均可以隨時來撩偶。



除文章外還有更多的團隊內容等你解鎖🔓
相關文章
相關標籤/搜索