flex布局中flex项在主轴的尺寸计算(flex布局主轴方向)
flex布局中flex项在主轴的尺寸计算
前言
本文仅参考最新flex规范,结合浏览器实际表现,介绍flex项在主轴尺寸的计算方式以及其限制条件,并阐明几个属性间的相互关系。
为方便起见这里回顾一些下文会提到的flex基本概念:
flex容器(flex container):设置display为flex或者inline-flex的元素
flex项(flex item):flex容器的子元素
主轴(main axis):flex容器flex-direction的值,默认是横向
掘金会自动过滤掉style样式,所以文中例子都改用图片代替,可以复制代码自行验证
flex项在主轴尺寸的计算方式
flex项的flex属性(flex-grow、flex-shrink、flex-basis)决定了当flex容器在主轴空间过多、不足时,在主轴方向的伸展、压缩方式。具体计算公式为
flex项主轴尺寸 = 伸缩基础(flex-basis) + 伸缩因子(flex factors) × 伸缩空间
伸缩基础即flex-basis属性值,若为auto则取flex项默认宽度。
伸缩因子的计算对应伸展还是压缩有区别,假设flex factors为Ff,flex-grow为Fg,flex-shrink为Fs,flex-basis为Fb,则
Ffi=Fgi∑i=0NFgiFf_i=\frac{Fg_i}{\sum_{i=0}^NFg_i}Ffi=∑i=0NFgiFgi Ffi=FsiFbi∑i=0NFsiFbiFf_i=\frac{Fs_iFb_i}{\sum_{i=0}^N Fs_iFb_i}Ffi=∑i=0NFsiFbiFsiFbi
压缩时,值为当前flex项的flex-shrink与flex-basis的积,除以同一主轴所有flex项的flex-shrink与flex-basis积的和,即
伸展时,值为当前flex项的flex-grow除以同一主轴所有flex项的flex-grow的和,即
伸缩空间是flex容器在主轴方向的尺寸,减去同一个主轴上所有flex项的flex-basis的和。当flex容器空间过多时伸缩空间为正数,反之为负数。
当同一主轴所有flex项的flex-grow或者flex-shrink之和sum小于1时,伸缩空间会变成原来的sum/1,伸缩因子计算方式不变。
例1,伸展:
<div style="display:flex; height: 100px; width: 500px; border:2px solid black; box-sizing: content-box;"> <div style="background:red; flex: 1 0 80px;"></div> <div style="background:blue; flex: 3 5 120px;"></div> <div style="background:green; flex: 6 2 200px;"></div> </div> 复制代码
在这个例子中,伸缩空间为 500 - ( 80 + 120 + 200 ) = 100px,空间过多需伸展。计算各个flex项宽度为
红色: 80 + 1 / 10 × 100 = 90px
蓝色:120 + 3 / 10 × 100 = 150px
绿色:200 + 6 / 10 × 100 = 260px
例2,压缩:
<div style="display:flex; height: 100px; width: 500px; border:2px solid black; box-sizing: content-box;"> <div style="background: red; flex: 1 3 100px;"></div> <div style="background: blue; flex: 3 1 200px;"></div> <div style="background: green; flex: 6 5 300px;"></div> </div> 复制代码
在上面的例子中,伸缩空间为500 - ( 100 + 200 + 300 ) = -100px,空间不够需压缩。计算各个flex项宽度为
红色:100 - ( 3 × 100 ) / ( 3 × 100 + 1 × 200 + 5 × 300 ) × 100 = 85px
蓝色:200 - ( 1 × 200 ) / ( 3 × 100 + 1 × 200 + 5 × 300 ) × 100 = 190px
绿色:300 - ( 5 × 300 ) / ( 3 × 100 + 1 × 200 + 5 × 300 ) × 100 = 225px
例3,不完整伸展:
<div style="display:flex; height: 100px; width: 500px; border:2px solid black; box-sizing: content-box;"> <div style="background: red; flex: 0.1 3 80px;"></div> <div style="background: blue; flex: 0.2 1 120px;"></div> <div style="background: green; flex: 0.5 5 200px;"></div> </div> 复制代码
在上面的例子中,初算伸缩空间为500 - ( 80 + 120 + 200 ) = 100px,空间过多需伸展,实际空间为 100 * ( 0.1 + 0.2 + 0.5 ) = 80px。计算各个flex项宽度为
红色: 80 + 0.1 / 0.8 × 80 = 90px
蓝色:120 + 0.2 / 0.8 × 80 = 140px
绿色:200 + 0.5 / 0.8 × 80 = 250px
例4,不完整压缩:
<div style="display:flex; height: 100px; width: 500px; border:2px solid black; box-sizing: content-box;"> <div style="background: red; flex: 1 0.3 100px;"></div> <div style="background: blue; flex: 3 0.1 200px;"></div> <div style="background: green; flex: 6 0.5 300px;"></div> </div> 复制代码
在上面的例子中,伸缩空间为500 - ( 100 + 200 + 300 ) = -100px,空间不够需压缩,实际空间为 -100 * ( 0.3 + 0.1 + 0.5 ) = -90px。计算各个flex项宽度为
红色:100 - ( 0.3 × 100 ) / ( 0.3 × 100 + 0.1 × 200 + 0.5 × 300 ) × 90 = 86.5px
蓝色:200 - ( 0.1 × 200 ) / ( 0.3 × 100 + 0.1 × 200 + 0.5 × 300 ) × 90 = 191px
绿色:300 - ( 0.5 × 300 ) / ( 0.3 × 100 + 0.1 × 200 + 0.5 × 300 ) × 90 = 232.5px
实际应用中应该避免使用小数设置flex-grow/flex-shrink
flex项在主轴尺寸的限制
flex项作为一个dom元素,同样受到min-width/height、max-width/height属性的限制。这里计算方式为 先按照正常的方式计算尺寸,然后考虑属性限制。一旦flex项的限制对尺寸产生影响,其将按照限制呈现。接着重新计算伸缩空间,排除产生限制的flex项后再次分配伸缩空间。
例5,伸展时最小尺寸限制,为例1的变种:
<div style="display:flex; height: 100px; width: 500px; border:2px solid black; box-sizing: content-box;"> <div style="background: red; flex: 1 3 80px; min-width: 105px;"></div> <div style="background: blue; flex: 3 1 120px;"></div> <div style="background: green; flex: 6 5 200px;"></div> </div> 复制代码
由例1得出红色尺寸本为90px,受到min-width影响后变为105px。伸缩空间变为500 - ( 105 + 120 + 200 ) = 75px,空间过多需伸展,蓝色、绿色分配剩余空间。计算各个flex项宽度为
红色:105px
蓝色:120 + 3 / ( 3 + 6 ) × 75 = 145px
绿色:200 + 6 / ( 3 + 6 ) × 75 = 250px
例6,伸展时最大尺寸限制,为例1的变种:
<div style="display:flex; height: 100px; width: 500px; border:2px solid black; box-sizing: content-box;"> <div style="background: red; flex: 1 3 80px;"></div> <div style="background: blue; flex: 3 1 120px;"></div> <div style="background: green; flex: 6 5 200px; max-width: 250px;"></div> </div> 复制代码
由例1得出绿色尺寸本为260px,受到max-width影响后变为250px。伸缩空间变为500 - ( 80 + 120 + 250 ) = 50px,空间过多需伸展,红色、蓝色分配剩余空间。计算各个flex项宽度为
红色: 80 + 1 / ( 1 + 3 ) × 50 = 92.5px
蓝色:120 + 3 / ( 1 + 3 ) × 50 = 157.5px
绿色:250px
例7,压缩时最小尺寸限制,为例2的变种:
<div style="display:flex; height: 100px; width: 500px; border:2px solid black; box-sizing: content-box;"> <div style="background: red; flex: 1 3 100px;"></div> <div style="background: blue; flex: 3 1 200px;"></div> <div style="background: green; flex: 6 5 300px; min-width: 250px"></div> </div> 复制代码
由例2得出绿色尺寸本为225x,受到min-width影响后变为250px。伸缩空间变为500 - ( 100 + 200 + 250 ) = -50px。计算各个flex项宽度为
红色:100 - ( 3 × 100 ) / ( 3 × 100 + 1 × 200 ) × 50 = 70px
蓝色:200 - ( 1 × 200 ) / ( 3 × 100 + 1 × 200 ) × 50 = 180px
绿色:250px
压缩时最大尺寸限制类似不再举例。
尺寸限制还可使本该伸展的变为压缩,压缩的变为伸展,具体不再举例,算法是一样的。
另外当min-width/height、max-width/height相互冲突时,以min-width/height为准。这是对于所有元素适用的规则。
flex项在主轴尺寸的默认最小限制
css2.2规范中规定了min-width/height的默认值(initial)值是0,也就是没有最小尺寸限制。但对于flex项,min-width/height的默认值被重写为auto。该值的表现为 当flex项有子元素时,子元素的尺寸和flex项的width/height属性两者较小的值会成为flex项主轴尺寸的默认最小限制。 (此为简化表述,详细表述见规范4.5条)
例8,压缩时子元素默认限制,为例2、例7的变种:
<div style="display:flex; height: 100px; width: 500px; border:2px solid black; box-sizing: content-box;"> <div style="background: red; flex: 1 3 100px;"></div> <div style="background: blue; flex: 3 1 200px;"></div> <div style="background: green; flex: 6 5 300px;"> <div style="width: 250px; height: 100%; border: 1px solid yellow;"></div> </div> </div> 复制代码
由例2得出绿色尺寸本为225x,由于子元素的存在最小尺寸限制为250px,所以实际表现与例7一致
红色:70px
蓝色:180px
绿色:250px
例9,压缩时子元素和width都存在时,默认限制取较小值,为例8的变种:
<div style="display:flex; height: 100px; width: 500px; border:2px solid black; box-sizing: content-box;"> <div style="background: red; flex: 1 3 100px;"></div> <div style="background: blue; flex: 3 1 200px;"></div> <div style="background: green; flex: 6 5 300px; width: 240px;"> <div style="width: 250px; height: 100%; border: 1px solid yellow;"></div> </div> </div> 复制代码
在例8基础上添加width属性,默认最小尺寸限制取较小的with,重新计算伸缩空间变为500 - ( 100 + 200 + 240 ) = -60px。计算各个flex项宽度为
红色:100 - ( 3 × 100 ) / ( 3 × 100 + 1 × 200 ) × 60 = 76px
蓝色:200 - ( 1 × 200 ) / ( 3 × 100 + 1 × 200 ) × 60 = 184px
绿色:240px
如果将width改为260px,则与例8一致;如果将绿色子元素删除,就会失去最小宽度限制,又与最初的例2一致。可自行浏览器调试,不再举例。
默认最小尺寸限制使flex项在应用flex-shrink时可能会破坏预期的效果,解决的办法就是设置min-width/height属性值为0来覆盖默认值。
flex-basis、width/height、主轴尺寸关系
1. flex-basis和width
当flex-basis是auto时,flex-basis取用width的原始值。除此之外两者再无关系。
2. flex-basis和flex项主轴尺寸
flex-basis定义的是flex项在主轴方向的伸缩基础,是上面提到的计算主轴尺寸公式的一个参数。如果没有其他条件限制,且flex-grow、flex-shrink都是0,那flex项主轴尺寸和flex-basis一致。
3. width和flex项主轴尺寸、flex容器主轴尺寸
因为flex-basis默认是auto,width属性被认为是设置flex-basis另一种方式,其实这个描述是不准确。首先,如上文所说width会影响默认最小尺寸限制。
例10,flex-basis和width对flex项主轴尺寸的差异:
<div style="display:flex; height: 100px; width: 500px; border:2px solid black; box-sizing: content-box;"> <div style="background: red; flex: 1 1 200px;"> <div style="width: 210px; height: 100%; border: 1px solid yellow;"></div> </div> <div style="background: blue; flex: 1 1 auto; width: 200px;"> <div style="width: 210px; height: 100%; border: 1px solid yellow;"></div> </div> <div style="background: green; flex: 2 2 300px;"></div> </div> 复制代码
该例中蓝色块通过width间接设置flex-basis,但width属性同时改变了默认最小宽度限制,所以与红色的宽度不一致。
其次,当flex容器没有指定宽度时,其宽度由各个flex项的宽度决定。在flex容器被浮动、绝对定位、嵌套flex或者设置了inline-flex等这种没有默认宽度时容易发生。
例11,flex-basis和width对flex容器在主轴尺寸的差异
<div style="display:inline-flex; height: 100px; border:2px solid black; box-sizing: content-box;"> <div style="background: red; flex: 1 1 200px;"> <div style="width: 100px; height: 100%;"></div> </div> <div style="background: blue; flex: 1 1 auto; width: 200px;"> <div style="width: 100px; height: 100%;"></div> </div> <div style="background: green; flex: 1 1 300px;"></div> </div> 复制代码
在这个例子中,设置了display:inline-flex的容器没有默认宽度,反过来只能由flex项决定。红色块由其子项撑开100px,蓝色块定义了200px,绿色块只设置了flex-basis,对容器的宽度贡献为0,故最终容器内容宽度为300px。
这个例子最终三者平分300px,尺寸都是100px,请思考下是什么原因。
对于IE
由于时代等原因,IE11对flex也只是部分支持,不是按照最新规范实现的。所以如果需要支持IE,使用flex就要留意IE兼容性问题。本文中的很多例子在IE11下就有不一样的表现,可以使用IE11来找不同。
作者:桂之風
链接:https://juejin.cn/post/7029222557756850184