前言
css 的 flex 彈性佈局,廣泛應用在當今的頁面開發中,其彈性伸縮的的靈活性非常擅長於開發有響應式需求的網站。但是有很多時候,其靈活性也會讓人疑惑,特別是確定尺寸時,基本上是靠不斷嘗試。為了在開發時更加得心應手,下面將以主軸方向上,也就是預設佈局方向上的成員寬度值計算為例子進行研究。
準備
flex 是彈性的伸縮佈局,具有伸和縮兩個特性,下面在研究時也以伸和縮兩種狀態來進行研究。
在開始之前,先說一下一些定義,類名 container 是 flex 容器, 類名 item 是 flex 的成員物件,每個成員物件內嵌了自已的內容。每個成員都設定了相應的外邊距、邊框、內邊距,為了方便表述把每個成員的外邊距寬度的左右寬度和表述為 margin,類似的表述為 border、padding,在下面的計算中會有提及。最後把每個成員的內容區域寬度表述為 content。在下面的例子中我們會發現,成員的最終寬度大小是由成員的 width flex-basic 和 content 值共同決定,在不同狀態下也會分別受到 flex-grow 和 flex-shrink 的影響
伸
下面的 flex 佈局中設定了 6 個成員,每個成員都設定了不同的 width flex-basic content flex-grow 值
<div class="wrapper">
<h1>test</h1>
<style>
.container { display: flex; background-color: #915151;}
.item { height: 80px; text-align: center; background-color: rgb(214, 120, 52);border-radius: 1em; border: 5px solid #000;text-align: left;margin: 15px; padding: 10px }
</style>
<div class="container">
<div class="item" style="flex-grow: 1;width: 100px; flex-basis: 0">
<div style="width: 40px"></div>
</div>
<div class="item" style="flex-grow: 0; width: 250px; flex-basis: 200px">
<div style="width: 300px"></div>
</div>
<div class="item" style="flex-grow: 2; flex-basis: 200px">
<div></div>
</div>
<div class="item" style="flex-grow: 2; width: 70px">
<div></div>
</div>
<div class="item" style="flex-grow: 2;">
<div style="font-family: monospace;">12345678910111213141516</div>
</div>
<div class="item" style="flex-grow: 3; width: 200px;">
<div style="width: 100px"></div>
</div>
</div>
</div>複製程式碼
下面是生成佈局的截圖,包括了每個成員的尺寸
最內層的區域就是成員的大小區域,也就是成員的寬度和高度值,從寬度值可以看出,有的元素的寬度值和成員的 content 大小一樣, 有的和成員的 width 值一樣,有的和兩者都不同,下面將通過公式來計算各個成員的最終寬度值。
由於不是每個成員都設定了相應的 flex-basis、width 和 內容的 width 值,所以我先定下下面的規則來補充缺失的引數值,設定 t-flex-basis 為轉化的 flex-basis 值, t-width 為轉化的 width 值,t-content 為轉化的 content 值
- 成員的 flex-basis 值存在, 那麼 t-flex-basis 值就是該值,如果不存在,那麼 t-flex-basis 值就是 t-width 的值
- 成員的 width 值存在, 那麼 t-width 的值就是該值,如果不存在,,那麼 t-width 值就是 t-content 的值
- 成員的 content 設定了 width 值, 那麼 t-content 的值就是該值,如果不存在, 那麼 t-content 值就是內部的首選最小尺寸值,像成員 3, 4 內部沒有字元,那麼其 t-content 就是 0,而成員 5 有一串不斷行的字元,我故意把字型設定為等寬字型,在我的瀏覽器中每個字元是 6.5px,這裡有 23 個字元,所以 t-content 就為 149.5px
通過以上規則,就可以把成員的引數值轉化為下表的資料
下面可以開始計算最終寬度值
計算步驟
- 獲取剩餘空間
- 分配剩餘空間
- 找出不符合分配的成員,重新計算分配空間
獲取剩餘空間
剩餘空間 = 容器可分配空間 - 每個成員(margin + border + padding + t-flex-basis)的和
在我這裡的容器寬度是 1349px,這個也是這裡的容器可分配空間
剩餘空間 = 1349 - (30 + 10 + 20 + 0) - (30 + 10 + 20 + 200) - (30 + 10 + 20 + 200) - (30 + 10 + 20+ 70) - (30 + 10 + 20 + 149.5) - (30 + 10 + 20 + 200) = 169.5
分配剩餘空間
分配的剩餘空間 = 剩餘空間 * (當前成員的 flex-grow) / 待分配成員的 flex-grow 值之和
item1 分配的剩餘空間 = 169.5 * 1 / (1 + 0 + 2 + 2 + 2 + 3) = 16.95
item2 分配的剩餘空間 = 169.5 * 0 / (1 + 0 + 2 + 2 + 2 + 3) = 0
item3 分配的剩餘空間 = 169.5 * 2 / (1 + 0 + 2 + 2 + 2 + 3) = 33.9
item4 分配的剩餘空間 = 169.5 * 2 / (1 + 0 + 2 + 2 + 2 + 3) = 33.9
item5 分配的剩餘空間 = 169.5 * 2 / (1 + 0 + 2 + 2 + 2 + 3) = 33.9
item6 分配的剩餘空間 = 169.5 * 3 / (1 + 0 + 2 + 2 + 2 + 3) = 50.85
找出不符合分配的成員,重新計算分配空間
所謂不符合分配的成員指的是 (分配的剩餘空間 + t-flex-basis) < min(t-content, t-width)
當成員不符合分配時,那麼該成員的最終值就是 t-content 和 t-width 中的最小值
否則成員的最終值就是:分配的剩餘空間 + t-flex-basis
通過比較可以得到 item1 不符合分配
item1 (0 + 16.95) < min(t-content,t-width) = 40
所以 item1 的最終寬度是 40
由於 item1 如果是按分配應該是 16.95,現在是 40,多佔據了 23.05
所以要重新分配空間,分配的空間中要減少相應比例的空間
item2 重新分配後的剩餘空間 = 0 - 23.05 * 0 / (0 + 2 + 2 + 2 + 3) = 0
item3 重新分配後的剩餘空間 = 33.9 - 23.05 * 2 / (0 + 2 + 2 + 2 + 3) = 28.78
item4 重新分配後的剩餘空間 = 33.9 - 23.05 * 2 / (0 + 2 + 2 + 2 + 3) = 28.78
item5 重新分配後的剩餘空間 = 33.9 - 23.05 * 2 / (0 + 2 + 2 + 2 + 3) = 28.78
item6 重新分配後的剩餘空間 = 50.85 - 23.05 * 3 / (0 + 2 + 2 + 2 + 3) = 43.17
通過比較可以得到 item2 不符合分配
item2 (0 + 200) < min(250, 300) = 250
所以 item2 的最終寬度是 250
由於 item2 如果是按分配應該是 200,現在是 250,多佔據了 50
所以要重新分配空間,分配的空間中要減少相應比例的空間
item3 重新分配後的剩餘空間 = 28.78 - 50 * 2 / (2 + 2 + 2 + 3) = 17.67
item4 重新分配後的剩餘空間 = 28.78- 50 * 2 / (2 + 2 + 2 + 3) = 17.67
item5 重新分配後的剩餘空間 = 28.78- 50 * 2 / (2 + 2 + 2 + 3) = 17.67
item6 重新分配後的剩餘空間 = 43.17 - 50 * 3 / (2 + 2 + 2 + 3) = 26.5
通過比較可以得到以上的成員都符號分配
item3 的最終寬度是 17.67 + 200 = 217.67
item4 的最終寬度是 17.67 + 70= 87.67
item5 的最終寬度是 17.67 + 149.5= 167.17
item6 的最終寬度是 26.5 + 200 = 226.5
可以看到計算結果和最終的結果是一致的
以上是關於在伸長狀態下成員的計算規則,那麼在收縮狀態下,上面的計算公式還能起作用嗎
縮
下面的 flex 佈局中設定了 6 個成員,每個成員都設定了不同的 width flex-basic content flex-shrink 值, 為了讓元素處於收縮狀態,我修改了成員的大小
<div class="wrapper">
<h1>test</h1>
<style>
.container {display: flex; background-color: #915151;}
.item {height: 80px; text-align: center; background-color: rgb(214, 120, 52); border-radius: 1em;border: 5px solid #000;text-align: left;margin: 15px;padding: 10px}
</style>
<div class="container">
<div class="item" style="flex-shrink: 1;width: 100px; flex-basis: 500px">
<div style="width: 40px"></div>
</div>
<div class="item" style="flex-shrink: 0; width: 250px; flex-basis: 300px">
<div style="width: 300px"></div>
</div>
<div class="item" style="flex-shrink: 2; flex-basis: 500px">
<div></div>
</div>
<div class="item" style="flex-shrink: 2; width: 400px">
<div></div>
</div>
<div class="item" style="flex-shrink: 2;">
<div style="font-family: monospace;">12345678910111213141516</div>
</div>
<div class="item" style="flex-shrink: 3; width: 300px;">
<div style="width: 100px"></div>
</div>
</div>
</div>複製程式碼
下面是生成佈局的截圖,包括了每個成員的尺寸
下面將通過公式計算成員最終的寬度值
按照前面提及的轉化規則,得到類似下面的表格
依據上面提到的計算步驟進行計算
獲取剩餘空間
剩餘空間 = 容器可分配空間 - 每個成員(margin + border + padding + t-flex-basis)的和
在我這裡的容器寬度是 1349px,這個也是這裡的容器可分配空間
剩餘空間 = 1349 - (30 + 10 + 20 + 500) - (30 + 10 + 20 + 300) - (30 + 10 + 20 + 500) - (30 + 10 + 20+ 400) - (30 + 10 + 20 + 149.5) - (30 + 10 + 20 + 300) = -1160.5
分配剩餘空間
分配的剩餘空間 = 剩餘空間 * (當前成員的 flex-shrink) / 待分配成員的 flex-shrink值之和
item1 分配的剩餘空間 = -1160.5 * 1 / (1 + 0 + 2 + 2 + 2 + 3) = -116.05
item2 分配的剩餘空間 = -1160.5 * 0 / (1 + 0 + 2 + 2 + 2 + 3) = 0
item3 分配的剩餘空間 = -1160.5 * 2 / (1 + 0 + 2 + 2 + 2 + 3) = -232.1
item4 分配的剩餘空間 = -1160.5 * 2 / (1 + 0 + 2 + 2 + 2 + 3) = -232.1
item5 分配的剩餘空間 = -1160.5 * 2 / (1 + 0 + 2 + 2 + 2 + 3) = -232.1
item6 分配的剩餘空間 = -1160.5 * 3 / (1 + 0 + 2 + 2 + 2 + 3) = -348.15
找出不符合分配的成員,重新計算分配空間
所謂不符合分配的成員指的是 (分配的剩餘空間 + t-flex-basis) < min(t-content, t-width)
當成員不符合分配時,那麼該成員的最終值就是 t-content 和 t-width 中的最小值
否則成員的最終值就是:分配的剩餘空間 + t-flex-basis
通過比較可以得到 item5 不符合分配
item5 (-232.1 + 149.5) < min(t-content,t-width) = 149.5
所以 item5 的最終寬度是 149.5
由於 item5 如果是按分配應該是 -82.6,現在是 149.5,多佔據了 232.1
所以要重新分配空間,分配的空間中要減少相應比例的空間
item1 重新分配後的剩餘空間 = -116.05 - 232.1 * 1 / (1 + 0 + 2 + 2 + 3) = -145.0625
item2 重新分配後的剩餘空間 = 0 - 232.1 * 0 / (1 + 0 + 2 + 2 + 3) = 0
item3 重新分配後的剩餘空間 = -232.1 - 232.1 * 2 / (1 + 0 + 2 + 2 + 3) = -290.125
item4 重新分配後的剩餘空間 = -232.1 - 232.1 * 2 / (1 + 0 + 2 + 2 + 3) = -290.125
item6 重新分配後的剩餘空間 = -348.15 - 232.1 * 3 / (1 + 0 + 2 + 2 + 3) = -435.1875
通過比較可以得到 item6 不符合分配
item6 (-435.1875 + 300) < min(300, 100) = 100
所以 item6 的最終寬度是 100
由於 item6 如果是按分配應該是 -135.1875,現在是 100,多佔據了 235.1875
所以要重新分配空間,分配的空間中要減少相應比例的空間
item1 重新分配後的剩餘空間 = -145.0625 - 235.1875 * 1 / (1 + 0 + 2 + 2) = -192.1
item2 重新分配後的剩餘空間 = 0 - 235.1875 * 0 / (1 + 0 + 2 + 2) = 0
item3 重新分配後的剩餘空間 = -290.125- 235.1875 * 2 / (1 + 0 + 2 + 2) = -384.2
item4 重新分配後的剩餘空間 = -435.1875 - 235.1875 * 2 / (1 + 0 + 2 + 2) = -529.2625
通過比較可以得到 item4 不符合分配
item4 (-529.2625 + 400) < min(t-content,t-width) = 0
所以 item4 的最終寬度是 0
但是可以看到 item4 實際的渲染結果不是 0,所以以上的計算公式在收縮狀態下是無效的,問題出現在計算剩餘空間的計算上,在收縮狀態下的計算公式是
分配的剩餘空間 = 剩餘空間 * (當前成員的 flex-shrink * t-flex-basis) / 待分配成員的(flex-shrink * t-flex-basis)之和
重新計算以上的步驟
分配剩餘空間
分配的剩餘空間 = 剩餘空間 * (當前成員的 flex-shrink * t-flex-basis) / 待分配成員的(flex-shrink * t-flex-basis)之和
item1 分配的剩餘空間 = -1160.5 * 1 * 500 / (1 * 500 + 0 * 300 + 2 * 500 + 2 * 400 + 2 * 149.5 + 3 * 300) = -165.83
item2 分配的剩餘空間 = -1160.5 * 0 * 300 / (1 * 500 + 0 * 300 + 2 * 500 + 2 * 400 + 2 * 149.5 + 3 * 300) = 0
item3 分配的剩餘空間 = -1160.5 * 2 * 500 / (1 * 500 + 0 * 300 + 2 * 500 + 2 * 400 + 2 * 149.5 + 3 * 300) = -331.666
item4 分配的剩餘空間 = -1160.5 * 2 * 400 / (1 * 500 + 0 * 300 + 2 * 500 + 2 * 400 + 2 * 149.5 + 3 * 300) = -265.33
item5 分配的剩餘空間 = -1160.5 * 2 * 149.5 / (1 * 500 + 0 * 300 + 2 * 500 + 2 * 400 + 2 * 149.5 + 3 * 300) = -99.168
item6 分配的剩餘空間 = -1160.5 * 3 * 300 / (1 * 500 + 0 * 300 + 2 * 500 + 2 * 400 + 2 * 149.5 + 3 * 300) = -298.5
找出不符合分配的成員,重新計算分配空間
所謂不符合分配的成員指的是 (分配的剩餘空間 + t-flex-basis) < min(t-content, t-width)
當成員不符合分配時,那麼該成員的最終值就是 t-content 和 t-width 中的最小值
否則成員的最終值就是:分配的剩餘空間 + t-flex-basis
通過比較可以得到 item5 不符合分配
item5 (-99.168 + 149.5) < min(t-content,t-width) = 149.5
所以 item5 的最終寬度是 149.5
由於 item5 如果是按分配應該是 50.33,現在是 149.5,多佔據了 99.17
所以要重新分配空間,分配的空間中要減少相應比例的空間
item1 分配的剩餘空間 = -165.83 - 99.17 * 1 * 500 / (1 * 500 + 0 * 300 + 2 * 500 + 2 * 400 + 3 * 300) = -181.325
item2 分配的剩餘空間 = 0 - 99.17 * 0 * 300 / (1 * 500 + 0 * 300 + 2 * 500 + 2 * 400 + 3 * 300) = 0
item3 分配的剩餘空間 = -331.666 - 99.17* 2 * 500 / (1 * 500 + 0 * 300 + 2 * 500 + 2 * 400 + 3 * 300) = -362.65
item4 分配的剩餘空間 = -265.33 - 99.17 * 2 * 400 / (1 * 500 + 0 * 300 + 2 * 500 + 2 * 400 + 3 * 300) = -290.1225
item6 分配的剩餘空間 = -298.5 - 99.17 * 3 * 300 / (1 * 500 + 0 * 300 + 2 * 500 + 2 * 400 + 3 * 300) = -326.39
通過比較可以得到 item6 不符合分配
item6 (-326.39 + 300) < min(300, 100) = 100
所以 item6 的最終寬度是 100
由於 item6 如果是按分配應該是 -26.39,現在是 100,多佔據了 126.39
所以要重新分配空間,分配的空間中要減少相應比例的空間
item1 分配的剩餘空間 = -181.325 - 126.39 * 1 * 500 / (1 * 500 + 0 * 300 + 2 * 500 + 2 * 400) = -208.8
item2 分配的剩餘空間 = 0 - 126.39 * 0 * 300 / (1 * 500 + 0 * 300 + 2 * 500 + 2 * 400) = 0
item3 分配的剩餘空間 = -362.65 - 126.39* 2 * 500 / (1 * 500 + 0 * 300 + 2 * 500 + 2 * 400) = -417.6
item4 分配的剩餘空間 = -290.1225 - 126.39 * 2 * 400 / (1 * 500 + 0 * 300 + 2 * 500 + 2 * 400) = -334.08
通過比較可以得到以上成員都符號要求
item1 的最終寬度是 500 - 208.8 = 291.2
item2 的最終寬度是 0 + 300= 300
item3 的最終寬度是 -417.6 + 500= 82.4
item4 的最終寬度是 -334.08 + 400 = 65.9
可以看到計算結果和最終的結果是一致的
總結
從以上的分析和計算中,可以看到成員的寬度受多個引數所控制,主要屬性是 flex-basis, 其中在伸長和收縮狀態中,計算分配的剩餘空間的公式是不同的,而且在計算中還會受到成員內容大小和成員自身大小的影響,成員的最終值不得小於成員內容大小和成員自身大小中的最小值,否則就取這個最小值並重新分配剩餘空間。這裡可以舉一個例子,很多時候我們設定 flex: 1 並希望成員都是等分的,一般情況下是沒有問題的。其中 flex: 1 實際上是 flex-basis: 0%, flex-grow: 1, flex-shrink: 1,如果你不能控制成員的內容大小,當這個內容尺寸大於等分尺寸時,由於沒有設定 width 值, width 值預設等於內容大小,按照以上的分析,該尺寸就會改變成員寬度,造成等分的效果失效,要避免這種情況,可以設定 width 的值為 0,這樣成員內容大小和成員自身大小中的最小值就為0,等分尺寸肯定大於該值,成員尺寸便取等分尺寸,當然這個時候成員內容就會溢位。
通過以上的分析,我們對計算成員寬度已經有初步的瞭解,基本上能應對大部分的情況,但是 flex 佈局還不止以上的屬性,而且不同的屬性之間還能互相組合,甚至在有的時候可能在容器上設定了絕對定位(flex功能不會消失),或者成員上設定了 box-sizing(設定的 width 值可能不同於讀取的 width 值),或者 width 值是百分比(父元素寬度不確定時,width 的表現像 auto,等父元素確定後才開始計算),flex-grow 或者 flex-shrink 是小數(滿足特定情況計算規則會改變)等,在以上這些情況下計算時要考慮因素會更多。
為了研究 flex 佈局規則,我特意寫了一個測試頁面,大家有興趣也可以去看一下
flex 佈局測試 changk99.github.io/flexbox/