最近看到一些关于flex的面试题,发现其中关于宽度计算的问题比较多,网上的解释也有些不太完整,于是自己总结了一下。若对 flex 不是很了解,可以看一下阮一峰老师的教程 Flex 布局教程:语法篇
flex 属性值
一般来说,使用 flex 时都是作为简写方式,类似于 background 属性可以包含 background-image background-repeat 等属性。flex 属性是 flex-grow , flex-shrink 和 flex-basis 的简写,默认值为 0 1 auto 。后两个属性可选。其中:
flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大;flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小;flex-basis属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。
flex 的属性介绍可以查看MDN,以下是部分摘抄:
flex 属性可以指定1个,2个或3个值。
单值语法: 值必须为以下其中之一:
- 一个无单位数(
<number>): 它会被当作<flex-grow>的值。- 一个有效的宽度(width)值: 它会被当作
<flex-basis>的值。- 关键字
none,auto或initial.双值语法: 第一个值必须为一个无单位数,并且它会被当作
<flex-grow>的值。第二个值必须为以下之一:
- 一个无单位数:它会被当作
<flex-shrink>的值。- 一个有效的宽度值: 它会被当作
<flex-basis>的值。三值语法:
- 第一个值必须为一个无单位数,并且它会被当作
<flex-grow>的值。- 第二个值必须为一个无单位数,并且它会被当作
<flex-shrink>的值。- 第三个值必须为一个有效的宽度值, 并且它会被当作
<flex-basis>的值。
涉及到flex的宽度计算有两种情况:
- 主轴有多余空间分配,需要给子项目增加宽度(或高度)
- 主轴没有多余空间分配,并且空间不足以支持所有项目的宽度,需要子项目 挤出 空间
有多余空间分配
举个栗子:
<div class="container">
<div class="left"></div>
<div class="center"></div>
<div class="right"></div>
</div>
<style>
* {
padding: 0;
margin: 0;
}
.container {
width: 600px;
height: 300px;
display: flex;
}
.left {
width: 120px;
flex: 1 2 140px;
background: red;
}
.center {
width: 100px;
flex: 2 1 auto;
background: yellow;
}
.right {
flex: 2 1 200px;
background: blue;
}
</style>
主轴上空器宽度为600px,子元素总基准值为:140px + 100px + 200px = 440px . 所以此时主轴上是有剩余空间可以分配的,剩余空间为 600px - 440px = 160px
注:MDN上有说明,当一个元素同时设置了 flex-basis 和 width 时,flex-basis 具有更高的优先级。所以上述计算 left 的宽度以 140 计算而不是 120
flex-grow 系数之和为: 1 + 2 + 2 = 5
所以各子项目额外分配的剩余空间为
- left: 160 * 1/5 = 32
- center: 160 * 2/5 = 64
- right: 160 * 2/5 = 64
最终各项目的宽度为:
- left: 140 + 32 = 172
- center: 100 + 64 = 164
- right: 200 + 64 = 264
主轴空间不足
话不多说,上代码:
<div class="container">
<div class="left"></div>
<div class="right"></div>
</div>
<style>
* {
padding: 0;
margin: 0;
}
.container {
width: 600px;
height: 300px;
display: flex;
}
.left {
flex: 1 2 500px;
background: red;
}
.right {
flex: 2 1 400px;
background: blue;
}
</style>
问:left、right 最终渲染时的宽度是多少?
首先容器宽度是600px,两个子项目的宽度以 flex-basis 计算为 500px + 400px = 900px , 很显然容器的空间是不足以完全放下两个子项目的。这个时候就需要 “缩减” 子项目的宽度来 “补偿” 不足的 300px (900px - 600px) 空间。
其次,当空间不足时,我们可以忽略 flex-grow 属性,因为该属性是在主轴有剩余空间时才起作用。
那如何 “补偿” 300px的空间呢:
-
每个项目的
flex-basis * flex-shrink- left: 500 * 2 = 1000
- right: 400 * 1 = 400
-
求和
- 1000 + 400 = 1400
-
求得每个项目需要腾出的空间。
- left: (1000 / 1400) * 300 = 214.29
- right: (400 / 1400) * 300 = 85.71
-
子项目减去腾出的空间即为最终宽度
- left: 500 - 214.29 = 285.71
- right: 400 - 85.71 = 314.29
所以最终渲染结果为 left 宽度为 285.71px, right 宽度为 314.29px
总结
在计算 flex 子项目的宽度时不要被 flex-basis 的值干扰了。flex-basis 首先是要被用来计算父容器是否有剩余空间可以被分配,然后再根据有无剩余空间取 flex-grow 或 flex-shrink 的值进行计算,最后再以 flex-basis 为宽度求和得到最后结果。