對于 flex-shrink 我們都知道它在 flex 布局中控制 flex 盒子空間不足時子元素改如何收縮,平常開發(fā)中更多的是使用默認值 1 或者設置 0。
那設置其他值的時候會有什么效果呢,不少文章中描述都不是很細,在很長一段時間我甚至以為自己是了解它的。
開篇我們帶著幾個問題
1. “flex-shrink
屬性定義了項目的縮小比例告抄,當父元素主軸方向空間不足的時候,子元素們按照 flex-shrink 的比例來縮小嵌牺〈蛲荩” 這句描述對嗎?
2. 一個父元素下有兩個子元素逆粹,兩個子元素各占用父元素 50% 且分別有 50px募疮、20px 的 padding。這個很簡單的需求用 flex 布局如何實現(xiàn)僻弹?如果嘗試以后和你的想象不同阿浓,那為什么會這樣呢?
3. 當空間不足時蹋绽,各項目具體會縮小多少芭毙?子元素 ``flex-shrink不同時有何影響筋蓖?子元素寬度會對縮小有影響嗎?父子元素的 margin稿蹲、padding扭勉、border 會對結(jié)果有影響嗎鹊奖?box-sizing 的值會有影響嗎苛聘?
如果你對以上的問題不能清楚的回答,或者嘗試以后發(fā)現(xiàn)和自己想象的不同忠聚,那這篇文章對于你可能會有一些用设哗。
首先我們看第一個問題
1. “
flex-shrink
屬性定義了項目的縮小比例,當父元素主軸方向空間不足的時候两蟀,子元素們按照flex-shrink
的比例來縮小网梢。” 這句描述對嗎赂毯?
這句話描述其實不準確战虏。
flex-shrink 決定了子元素縮小系數(shù),但在具體計算的時候党涕,其實它還受到了 flex base size 的影響烦感。
w3c 對于的 flex-shrink 的描述有這樣一段備注
Note: The flex shrink factor is multiplied by the flex base size when distributing negative space. This distributes negative space in proportion to how much the item is able to shrink, so that e.g. a small item won’t shrink to zero before a larger item has been noticeably reduced.
從中我們可以看到,真正使用的縮小系數(shù)其實是 flex shrink factor * flex base size膛堤。下面我們用一個例子來說明它
[](javascript:void(0); "復制代碼")
<pre style="margin: 0px; padding: 0px; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;"><style> .box { display: flex; width: 400px; outline: 1px red solid;
} .item1 { flex: 0 2 300px; background-color: #32d6d6;
} .item2 { flex: 0 1 200px; background-color: #e2a83e;
} .item3 { flex: 0 2 100px; background-color: #b85ad0;
}
</style> ... <div class="box">
<div class="item1">1</div>
<div class="item2">2</div>
<div class="item3">3</div>
</div> </pre>
](javascript:void(0); "復制代碼")
按照不準確的描述 flex-shrink` 決定了子元素縮小系數(shù)手趣,那我們知道子元素需要的空間是 300+200+100 一共 600px,但父元素只有 400px 所以分別的負空間是 200px肥荔,或者說需要縮小 200px绿渣。三個元素
flex-shrink` 分別為 2 1 2,表面上看應該分別縮小 80 40 80燕耿,那三個元素應該 220 160 20中符。但事實是這樣嗎?
如果你也嘗試一下誉帅,就會知道舟茶,實際上的效果是 180 160 60。
我們來看一下正確的計算方式:
flex-shrink * flex-base(姑且先這么寫堵第,之后會修正) => factor
2 * 300 => 600
1 * 200 => 200
2 * 100 => 200
所以三個元素真正的系數(shù)分別是 600/1000 200/1000 200/1000吧凉。200 的總額得出 120 40 40√ぶ荆可以看到和實際情況相符阀捅。
按照這個公式可以滿足多數(shù)情況的使用,但其中還隱藏著其他規(guī)則针余。下面我們看第二個問題
2. 一個父元素下有兩個子元素饲鄙,兩個子元素各占用父元素 50% 且分別有 50px凄诞、20px 的 padding。這個很簡單的需求用 flex 布局如何實現(xiàn)忍级?如果嘗試以后和你的想象不同帆谍,那為什么會這樣呢?
該問題其實是我發(fā)現(xiàn)自己對 flex-shrink` 不夠了解轴咱,從而研究的原因汛蝙。` 這個問題看起來很簡單吧,估計多數(shù)人第一反應是這樣:
](javascript:void(0); "復制代碼")
<pre style="margin: 0px; padding: 0px; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;"><style> .box { display: flex; width: 400px; outline: 1px red solid;
} .item-2-1 { flex: 1 1; padding: 50px; background-color: #32d6d6; background-clip: content-box;
} .item-2-2 { flex: 1 1; padding: 20px; background-color: #b85ad0; background-clip: content-box;
}
</style> ... <div class="box">
<div class="item-2-1">1</div>
<div class="item-2-2">2</div>
</div></pre>
](javascript:void(0); "復制代碼")
看起來收縮朴肺、放大系數(shù)都相等窖剑,兩個元素應該父元素 400 像素,每個 200 對吧戈稿?我們看一下實際情況
是不是和想象不同西土?
下面說明原因
w3c 里對于元素可用長度有這樣的描述
dimension of the flex container’s content box is a definite size, use that; if that dimension of the flex container is being sized under a min or max-content constraint, the available space in that dimension is that constraint; otherwise, subtract the flex container’s margin, border, and padding from the space available to the flex container in that dimension and use that value. This might result in an infinite value.
我們可用看到其實計算主軸可以用長度的計算是要去除 margin, border, and padding 的,但這里的描述我覺得其實也不準確鞍盗,他這里說的只是 flex container需了,似乎只是父元素里的 margin、border般甲、padding肋乍。但在我的實際測試的時候,其實還包括更多
比如:直接子元素的 margin欣除、border住拭、padding 甚至是直接子元素的 min-width,稍緩我會說為什么
那么我們來計算一下
400 - 502 - 202 = 260(可用空間)
flex-base 為 0历帚,flex-grow 都為 1滔岳;260*(1/2)= 130
130 + 20 + 20 = 170
那么我們要如何實現(xiàn)子元素含 padding 時也平分空間呢?
flex-base 的描述里有這樣的一句
As another corollary, flex-basis determines the size of the content box, unless otherwise specified such as by box-sizing [CSS3UI].
可以看到 flex-basis 的數(shù)值設置的 width 其實是 box-sizing 的默認值 box-sizing: content-box; 那么理所當然會想到修改參數(shù)挽牢,改為 box-sizing: border-box; 然后 flex-base: 100%;
如此一來谱煤,兩個元素都如 IE 盒模型一樣,寬度包含 border padding禽拔,而且收縮刘离、放大系數(shù)都一樣,是不是就可以實現(xiàn)需求了呢睹栖?答案還是否定的硫惕,不但實現(xiàn)不了需求,甚至會出現(xiàn)一時間難以理解的數(shù)值
上面提到子元素的 padding 等值也會算在不可伸縮長度里凍結(jié)掉野来。為什么這么說呢恼除,我們結(jié)合上圖的原因來做解說
這個值是怎么來的呢,其經(jīng)過了以下的步驟
1. 計算子元素 flex-base 所代表的實際值 => 400px
2. 那兩個就是 800px,父元素 400px豁辉,主軸長度不夠令野,flex-shrink 開始生效。但第二步卻不是 flex-shrink * flex-base 得出真正的比例系數(shù)徽级,我們需要先得到“真正的” flex base size气破,其實之前我們提及過
事實上,真正的 flex base size 并非單純的是 flex-base餐抢。更準確的說现使,子元素 flex-base 設置后帶來的 content width。比如這里 flex base size = box width 400 - padding 202 - border 0 = 360弹澎,以及兩外一個 400-502=300朴下。
3. 計算比例系數(shù) 3601=360努咐,3001=300苦蒿。所有其比例系數(shù)分別是 300/660、360/660渗稍。
4. 計算需要分配的負空間 4002-400 = 400px
5. 計算分別需要縮減的部分 400(300/660)≈181.8181佩迟、400*(360/660)≈218.1818
6. 實際寬度 400-181.8181≈218.18 400-21≈181.81
對于其原因,w3c 里對于如何計算彈性長度有這樣的一個描述
> Size inflexible items. Freeze, setting its target main size to its hypothetical main size…
對此我是這樣理解的竿屹,在計算子元素主軸長度的時候存璃,有這么一些操作
把 flex container 的 margin玉罐、border、padding 所占的長度凍結(jié),因為這些不可分配
把 子元素的 margin袍冷、border、padding 所占的空間凍結(jié)奴璃,因為這部分不會參與伸縮
剩下的空間才會作為正瞒滴、負長度分配給子元素
所以我們得出更詳細的壓縮計算公式
flex_container_available_length = flex_container_content_width(or height)
flex_items_length = flex_item_box_width + flex_item_box_width + flex_item_box_width...
shrink_factor = (flex-shrink * flex_base_size) /((flex-shrink * flex_base_size) + (flex-shrink * flex_base_size) + ...)
will_allocate_length = flex_container - flex_items_length
flex_item_width = flex_item_box_width + flex_item_box_width * (will_allocate_length * shrink_factor)
注1:flex_item_box_width = margin + padding + border + content width
注2: flex_base_size 取決于該元素的 box-sizing 和 flex-base,其值為 border-box 時哮缺,flex_base_size = flex-base - padding - barder弄跌;其值為 content-box 時,flex_base_size = flex-base尝苇。
為了驗證公式的正確性铛只,我們隨意設計一個 margin padding border box-sizing flex-shrink flex-base 多樣繁雜的 demo(.flexBox)
[](javascript:void(0); "復制代碼")
<pre style="margin: 0px; padding: 0px; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;"><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>flex-shrink demo</title>
<style> .box { display: flex; width: 400px; outline: 1px red solid;
} .item1 { flex: 0 2 300px; background-color: #32d6d6;
} .item2 { flex: 0 1 200px; background-color: #e2a83e;
} .item3 { flex: 0 2 100px; background-color: #b85ad0;
} .item-2-1 { flex: 1 1; padding: 50px; background-color: #32d6d6; background-clip: content-box;
} .item-2-2 { flex: 1 1; padding: 20px; background-color: #b85ad0; background-clip: content-box;
} .demo-3 { flex: 1 1; display: flex; align-items: center;
} .demo-3-1 { flex: 1 1; padding: 50px; background-color: #32d6d6; background-clip: content-box;
} .demo-3-2 { flex: 1 1; padding: 20px; background-color: #b85ad0; background-clip: content-box;
} .item-border-box { flex-basis: 100%; box-sizing: border-box;
} .flexBox { display: flex; width: 1500px; outline: 1px red solid;
} .flexItem-1, .flexItem-2, .flexItem-3 { flex-shrink: 2; flex-basis: 300px;
} .flexItem-1 { margin: 0 10px;
} .flexItem-2 { padding: 0 20px; border: 5px #ccc solid; box-sizing: content-box;
} .flexItem-3 { padding: 0 20px; border: 5px #ccc solid; box-sizing: border-box;
} .flexItem-4, .flexItem-5, .flexItem-6 { flex-shrink: 1; flex-basis: 200px;
} .flexItem-4 { padding: 0 10px;
} .flexItem-5 { border: 5px #ccc solid; margin: 0 10px; box-sizing: content-box;
} .flexItem-6 { border: 5px #ccc solid; margin: 0 10px; box-sizing: border-box;
} .flexItem-7, .flexItem-8, .flexItem-9 { flex-shrink: 2; flex-basis: 100px;
} .flexItem-7 { border: 5px #ccc solid;
} .flexItem-8 { padding: 0 30px; margin: 0 10px; box-sizing: content-box;
} .flexItem-9 { padding: 0 30px; margin: 0 10px; box-sizing: border-box;
}
</style>
</head>
<body>
<div class="box">
<div class="item1">1</div>
<div class="item2">2</div>
<div class="item3">3</div>
</div>
<br />
<div class="box">
<div class="item-2-1">1</div>
<div class="item-2-2">2</div>
</div>
<br />
<div class="box">
<div class="demo-3">
<div class="demo-3-1"> 1 </div>
</div>
<div class="demo-3">
<div class="demo-3-2"> 2 </div>
</div>
</div>
<br />
<div class="box">
<div class="item-2-1 item-border-box">1</div>
<div class="item-2-2 item-border-box">2</div>
</div>
<br />
<div class="flexBox">
<div class="flexItem-1" title="300 - (550*600/2770) ≈ 180.866">1</div>
<div class="flexItem-2" title="300 - (550*600/2770) ≈ 180.866">2</div>
<div class="flexItem-3" title="300 - (550*500/2770) ≈ 200.722 - 20*2 - 10*2 = 150.722">3</div>
<div class="flexItem-4" title="200 - (550*200/2770) ≈ 160.288">4</div>
<div class="flexItem-5" title="200 - (550*200/2770) ≈ 160.288">5</div>
<div class="flexItem-6" title="200 - (550*190/2770) ≈ 162.274 - 5*2 = 152.274">6</div>
<div class="flexItem-7" title="100 - (550*200/2770) ≈ 60.288">7</div>
<div class="flexItem-8" title="100 - (550*200/2770) ≈ 60.288">8</div>
<div class="flexItem-9" title="100 - (550*80/2770) ≈ 84.115 - 30*2 = 24.115">9</div>
</div>
<!-- flex_container_available_length = 1500
flex_items_length = (300+10*2) + (300+20*2+5*2) + (300) + (200+10*2) + (200+10*2+5*2) + (200+10*2) + (100+5*2) + (100+10*2+30*2) + (100+10*2)
= 2050
shrink_factor = 2770
300*2 600
300*2 600
(300-20*2-5*2)*2 500
200*1 200
200*1 200
(200-5*2)*1 190
100*2 200
100*2 200
(100-30*2)*2 80
will_allocate_length = 1500 - 2050 = -550
flex_item_width
300 - (550*600/2770) = 180.866
300 - (550*600/2770) = 180.866
300 - (550*500/2770) = 200.722 - 20*2 - 10*2 = 150.722
200 - (550*200/2770) = 160.288
200 - (550*200/2770) = 160.288
200 - (550*190/2770) = 162.274 - 5*2 = 152.274
100 - (550*200/2770) = 60.288
100 - (550*200/2770) = 60.288
100 - (550*80/2770) = 84.115 - 30*2 = 24.115 -->
</body>
</html></pre>
[](javascript:void(0); "復制代碼")
上面的代碼包括文章中所有的 demo 的代碼,和第二個問題里說的需求的解決方法(其實巨簡單)
``文章里留下的另一個坑糠溜,min-width 會對計算有什么影響呢淳玩?這個問題留給你自己嘗試思考吧。如果想不通非竿,也歡迎留言討論蜕着。
最后,附上資料, 同時感謝stackoverflow的幫助汽馋。``
轉(zhuǎn)自: https://www.cnblogs.com/liyan-web/p/11217330.html