Flex Property

低版本 IE 一直是前端开发的黑洞,从今年起公司终于放弃了 IE 这个低能儿。最近可以大刀阔斧地使用一些 CSS3 新属性了,从简单的了解到使用,还得花一段时间好好琢磨一下。

flex 属性是 flex-grow, flex-shrinkflex-basis 三个属性的简写形式,必须应用在弹性盒模型(设置了 display: flex)元素的子元素上,否则 flex 属性是不起作用的。

语法格式如下:

flex: <flex-grow> <flex-shrink> <flex-basis>

flex-basis

flex-basis 指明了元素的宽度,相当于 width 这个属性。如果flex-basis 和 width 两个属性同时存在,则 flex-basis 会覆盖 width 的值。

如果 flex-basis 设为 auto,则 width 属性生效。

1
2
3
4
<div class="flex-box">
<div class="item-1">item-1</div>
<div class="item-2">item-2</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.flex-box {
width: 400px;
height: 300px;
display: flex;
justify-content: center;
align-items: center;
}

.item-1 {
width: 200px;
height: 200px;
flex-basis: 100px;
}

.item-2 {
height: 200px;
flex-basis: 200px;
}

在上面这个的结构里,item-1 元素设置了 width 为 200px,但是 flex-basis 设置为 100px 覆盖了 width 的值。实际显示出的宽度也为 100px。

flex-grow

该属性决定了当父元素宽度大于子元素宽度之和时,子元素的宽度该如何改变。

flex-grow 默认值为 0

flex-grow 的默认值为 0,说明子元素在任何情况下宽度都不变。就像上边的例子一样,子元素将保持 flex-basis 中设置的宽度不变,而不会进行放大。

flex-grow 大于 0

flex-grow 如果不为 0,则表示当父元素宽度大于子元素宽度之和时,子元素宽度将增加,而增加多少则取决于 flex-grow 的值。

仍以上边的例子为例,父元素宽为 400px,两个子元素宽度分别为 200px 和 100px,剩余 100px (400px - 200px - 100px)。如果子元素的 flex-grow 的值大于 0,则会对剩余的 100px 进行分割。

如果 item-1 设置 flex-grow 为 1,item-2 设置 flex-grow 为 2,那么两个子元素分别按照 1:2 的比例对剩余宽度进行分割。

item-1 将得到剩余的宽度为: 100px 1/(1+2) = 33.3px
item-2 将得到剩余的宽度为: 100px
2/(1+2) = 66.7px

最终,
item-1 的总宽度为:100px + 33.3px = 133.3px
item-2 的总宽度为:200px + 66.7px = 266.7px

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.flex-box {
...
width: 400px;
height: 300px;
}

.item-1 {
height: 200px;
flex-basis: 100px;
flex-grow: 1;
}

.item-2 {
height: 200px;
flex-basis: 200px;
flex-grow: 2;
}

只有设置 flex-grow 属性大于 0 的元素会对剩余空间进行分割,例如 item-1 设为 1, item-2 默认为 0。则 item-1 的最终宽度为 100px + 100px(剩余宽度) = 200px,而 item-2 则保持原大小不变。

flex-shrink

该属性决定了当父元素宽度小于子元素宽度之和时,子元素的宽度该如何改变。

flex-shrink 默认值为 1

flex-shrink 的默认值为 1 而不是 0!如果所有子元素都采用默认值 1,那么将按照子元素的宽度比例等比缩小。

flex-shrink 为 0

flex-shrink 等于 0 时,子元素宽度不变。

在下面这个例子中,item-1 的宽度为 300px,item-2 的宽度为 200px,尽管两个子元素宽度之和超过了父元素的 400px 宽度,但子元素均设置了 flex-shrink: 0,所以宽度保持不变,结果仍然会超出父元素的宽度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.flex-box {
...
width: 400px;
height: 300px;
}

.item-1 {
height: 200px;
flex-basis: 300px;
flex-shrink: 0;
}

.item-2 {
height: 200px;
flex-basis: 200px;
flex-shrink: 0;
}
flex-shrink 大于 0

flex-shrink 如果大于 0,则表示当父元素宽度小于子元素宽度之和时,子元素宽度将减小到与父元素相同,而每个子元素减小多少则取决于 flex-shrink 的值。

仍以上边的例子为例,父元素宽为 400px,两个子元素宽分别为 300px 和 200px,超出父元素 100px (300px + 200px - 400px)。如果子元素的 flex-shrink 的值大于 0,则会对超出的 100px 进行分割。

如果 item-1 设置 flex-shrink 为 3,item-2 设置 flex-shrink 为 2,那么按照刚才的 flex-grow 计算原则,是不是两个子元素像下面这样,分别按照 3:2 的比例对超出部分进行缩小呢?

item-1 缩小: 100 3/(3+2) = 60px
item-2 缩小: 100
2/(3+2) = 40px

最终,
item-1 的总宽度为:200px - 60px = 140px
item-2 的总宽度为:300px - 40px = 260px

答案是:并不是!!!!

flex-shrink 的值谜之诡异,因为它不仅与本身的值有关,还与元素的宽度有关!!!!

正确的计算方法是这样的:

item-1 缩小: 100px [ 3\200px / (3*200px + 2300px)] = 50px
item-2 缩小: 100px
[ 2*300px / (3*200px + 2*300px)] = 50px

最终,
item-1 的总宽度为:200px - 50px = 150px
item-2 的总宽度为:300px - 50px = 250px

这里比较难理解的是 3*200px / (3*200px + 2*300px),你可以将 200 和 300 想象为权重,各子元素的“地位”并不是平等的(平等时,直接使用 3/(3+2) 即可),当子元素宽度越大,那么它缩小的比例也应该相应增大。所以在计算缩小的大小时,还要把元素宽度这个权重因素考虑进去。

如果不设置 flex-shrink 的属性值,那么所有元素默认为 1,意味着元素按照本身宽度的比例进行缩小,宽度越大的元素,当然缩小的也就越多。

几个常见的属性值

flex 的默认值为 0 1 auto,表明子元素最大不会超过自身的宽度(width),如果父元素宽度比子元素宽度之和小,则会进行等比缩小。

另外,flex 还有两个可识别的属性值,一个是 auto,一个是 none。

1
flex: auto;

等价于

1
flex: 1 1 auto;

如果子元素未设置宽度,且 flex 属性设置均为 auto,那么子元素将完全平分父元素宽度。

如果子元素设置了宽度,且 flex 属性设置均为 auto,则按照子元素的宽度比例进行缩放。

1
flex: none;

等价于

1
flex: 0 0 auto;

如果子元素未设置宽度,且 flex 属性设置均为 none,那么子元素宽度与内容宽度相同,内容越长,宽度越大。

如果子元素设置了宽度,且 flex 属性设置均为 none,则严格按照 width 属性进行显示。

一针见血的总结

flex 属性之所以很难理解,是因为 flex-growflex-shrink 之间存在着一个非常大的不同,flex-shrink 不仅依赖于属性值,还依赖于元素本身的大小,但 flex-grow 只与属性值有关。

Happy Ending。