深入理解flexgrow、flexshrink、flexbasis

NO IMAGE

1. Flex佈局

Flex 是 Flexible Box 的縮寫,意為”彈性佈局”,用來為盒狀模型提供最大的靈活性
flex屬性是flex-grow, flex-shrink 和 flex-basis的簡寫,默認值為 0 1 auto

任何一個容器都可以用 Flex 進行佈局(如果不會 flex 佈局的可見阮老師的 Flex 佈局教程),而且 Flex 是發生在父容器和子容器之間的佈局關係的,那麼父容器與子容器的關係是怎麼樣子的,又是怎麼計算子容器所佔用的空間的呢,怎麼進行彈性佈局的呢?

欲要解決上面的問題,首先得了解 flex-grow 和 flex-shrink 是怎麼計算的?flex-basis 和 width 又有什麼關係和區別?

接下來,我們先提出兩個概念:剩餘空間和溢出空間,具體是什麼意思我們後面慢慢解釋。

2. flex-grow

flex-grow屬性在MDN上的定義是:

定義彈性盒子項(flex-item)的拉伸因子,默認值0”

傳統的佈局是子容器在父容器中從左到右進行佈局,應用 flex 進行佈局,那麼父容器一定設置 display: flex,子容器要“佔有”並且“瓜分”父容器的空間,如何佔有、瓜分的策略就是彈性佈局的策略。這裡就要解釋到“剩餘空間”的概念:

子容器在父容器的“主軸”上還有多少空間可以“瓜分”,這個可以被“瓜分”的空間就叫做剩餘空間。

文字總是很抽象,舉個例子就能理解剩餘空間了,現在有如下的代碼:

HTML 代碼:

<div class="container">
<div class="item a">
<p>A</p>
<p> width:100</p>
</div>
<div class="item b">
<p>B</p>
<p> width:150</p>
</div>
<div class="item c">
<p>C</p>
<p> width:100</p>
</div>
</div>

CSS代碼:

.container {
margin:10px;
display: flex;
width: 500px;
height: 200px;
background-color: #eee;
color: #666;
text-align: center;
}
.item {
height: 100px;
}
.item p {
margin: 0;
}
.a{
width: 100px;
background-color:#ff4466;
}
.b{
width: 150px;
background-color:#42b983;
}
.c{
width: 100px;
background-color:#61dafb;
}

展示的效果如下(最後那個框是截圖的時候的標註,不是展示出來的效果):

深入理解flexgrow、flexshrink、flexbasis

一圖勝千言,看到這個圖應該就明白什麼是剩餘空間了。

父容器的主軸還有這麼多剩餘空間,子容器有什麼辦法將這些剩餘空間瓜分來實現彈性的效果呢?

這就需要用到flex-grow 屬性了,flex-grow 定義子容器的瓜分剩餘空間的比例,默認為 0,即如果存在剩餘空間,也不會去瓜分。

flex-grow例子,將上面的例子改成如下代碼:

HTML 代碼(代碼只增加了 flex-grow 的說明,沒有其他結構的變動):

<div class="container">
<div class="item a">
<p>A</p>
<p> width:100</p>
<p>flex-grow:1</p>
</div>
<div class="item b">
<p>B</p>
<p> width:150</p>
<p>flex-grow:2</p>
</div>
<div class="item c">
<p>C</p>
<p> width:100</p>
<p>flex-grow:3</p>
</div>
</div>

CSS 代碼(給每個子容器增加了 flex-grow):

.container {
margin:10px;
display: flex;
width: 500px;
height: 200px;
background-color: #eee;
color: #666;
text-align: center;
}
.item {
height: 100px;
p {
margin: 0;
}
}
.a{
width: 100px;
flex-grow:1;
background-color:#ff4466;
}
.b{
width: 150px;
flex-grow:2;
background-color:#42b983;
}
.c{
width: 100px;
flex-grow:3;
background-color:#61dafb;
}
結果如下:

深入理解flexgrow、flexshrink、flexbasis

最初,我們發現,子容器的寬度總和只有 350px,父容器寬度為 500px,那麼剩餘空間就出現了,為 150px。當設置了 flex-grow 之後, A,B,C三個子容器會根據自身的 flex-grow 去“瓜分”剩餘空間。

在這裡我們總結為 flex-grow 屬性決定了子容器要佔用父容器多少剩餘空間。

計算方式如下:
  • 剩餘空間:x
  • 假設有三個flex item元素,flex-grow 的值分別為a, b, c
  • 每個元素可以分配的剩餘空間為:
    a/(a+b+c) * x,b/(a+b+c) * x,c/(a+b+c) * x

以 A 為例子進行說明: A 佔比剩餘空間:1/(1+2+3) = 1/6,那麼 A “瓜分”到的 150*1/6=25,實際寬度為100+25=125

考慮是否可以把 flex-grow 設置的值小於 1,而且 flex-grow 的和也小於 1 呢?只要把上面公式的分母(flex-grow 的和)設置為 1 就好啦!

3. flex-shrink

說完 flex-grow,我們知道了子容器設置了 flex-grow 有可能會被拉伸。那麼什麼情況下子容器被壓縮呢?考慮一種情況:如果子容器寬度超過父容器寬度,即使是設置了 flex-grow,但是由於沒有剩餘空間,就分配不到剩餘空間了。這時候有兩個辦法:換行和壓縮。由於 flex 默認不換行,那麼壓縮的話,怎麼壓縮呢,壓縮多少?此時就需要用到 flex-shrink 屬性了。

flex-shrink屬性在MDN上的定義是:

指定了 flex 元素的收縮規則,默認值是 1

此時,剩餘空間的概念就轉化成了“溢出空間”

計算方式:
  • 三個flex item元素的width: w1, w2, w3
  • 三個flex item元素的flex-shrink:a, b, c
  • 計算總壓縮權重:
    sum = a * w1 + b * w2 + c * w3
  • 計算每個元素壓縮率:
    S1 = a * w1 / sum,S2 =b * w2 / sum,S3 =c * w3 / sum
  • 計算每個元素寬度:width – 壓縮率 * 溢出空間
舉例說明:
<div class="container">
<div class="item a">
<p>A</p>
<p> width:300</p>
<p>flex-shrink: 1</p>
</div>
<div class="item b">
<p>B</p>
<p> width:150</p>
<p>flex-shrink: 2</p>
</div>
<div class="item c">
<p>C</p>
<p> width:200</p>
<p>flex-shrink: 3</p>
</div>
</div>
.container {
margin:10px;
display: flex;
width: 500px;
height: 200px;
background-color: #eee;
color: #666;
text-align:center;
}
.item {
height: 100px;
}
.item p {
margin: 0;
}
.a{
width: 300px;
flex-grow: 1;
flex-shrink: 1;
background-color:#ff4466;
}
.b{
width: 150px;
flex-shrink: 2;
background-color:#42b983;
}
.c{
width: 200px;
flex-shrink: 3;
background-color:#61dafb;
}

子容器寬度總和為650,溢出空間為150
總壓縮:300 * 1 + 150 * 2 + 200 * 3 = 1200
A的壓縮率:300*1 / 1200 = 0.25
A的壓縮值:150 * 0.25 = 37.5
A的實際寬度:300 – 37.5 = 262.5

結果如下:

深入理解flexgrow、flexshrink、flexbasis

同樣,如果出現flex-shrink總和小於1?那麼計算溢出空間(收縮總和)的結果有所變化。比如:shrink設置為0.1, 0.2, 0.3, 原溢出空間為200,實際溢出空間:200 * (0.1 + 0.2 + 0.3)/ 1 = 120。

注意:如果子容器沒有超出父容器,設置 flex-shrink 無效

4. flex-basis

MDN定義:指定了 flex 元素在主軸方向上的初始大小

一旦 flex item 放進 flex 容器,並不能保證能夠按照 flex-basis 設置的大小展示。瀏覽器會根據 flex-basis 計算主軸是否有剩餘空間。既然是跟寬度相關,那麼 max-width,min-width,width 和 box 的大小優先級是怎麼樣的。

舉例說明:
<div class="container">
<div class="item a">A</div>
<div class="item b">B</div>
<div class="item c">C</div>
</div>
.container {
margin:10px;
display: flex;
width: 500px;
height: 200px;
background-color: #eee;
text-align: center;
line-height: 100px;
color: #666;
}
.item {
width: 100px;
height: 100px;
}
.a{
flex-basis: 200px;
background-color:#ff4466;
}
.b{
max-width: 50px;
flex-basis: 150px;
background-color:#42b983;
}
.c{
background-color:#61dafb;
}
結果如下:

深入理解flexgrow、flexshrink、flexbasis

上面的例子可以通過最終元素的寬度看出幾個屬性的優先級關係:

max-width/min-width > flex-basis > width > box

5. 應用場景

  1. 一種很常見的佈局:當內容區域高度不夠的時候,footer仍然需要固定在底部。這時候,我們可以給main使用flex-grow: 1,使它自動填滿剩餘空間。
深入理解flexgrow、flexshrink、flexbasis

2 . 在我們開發一種常見的表單組件的時候,使用flex佈局,可以使輸入框佔滿剩餘空間。

深入理解flexgrow、flexshrink、flexbasis

而大部分場景下我們不希望元素被壓縮,所以flex-shrink通常設置為0。

6. 總結

最後,我們需要注意的是:

  • flex items 總和超出 flex 容器,會根據 flex-shrink 的設置進行壓縮
  • 如果有剩餘空間,如果設置 flex-grow,子容器的實際寬度跟 flex-grow 的設置相關。如果沒有設置flex-grow,則按照 flex-basis 展示實際寬度

參考文獻:

相關文章

為啥HashMap的默認容量是16?

flutter好用的輪子推薦二十flutter仿iPhone鎖屏界面

新的API可以讓網頁直接讀寫硬盤上的文件?

手摸手教你寫個ESLint插件以及瞭解ESLint的運行原理