阅读 111

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,伸展:

l01.png

<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,压缩:

l02.png

<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,不完整伸展:

l03.png

<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,不完整压缩:

l04.png

<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的变种:

l05.png

<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的变种:

l06.png

<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的变种:

l07.png

<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的变种:

l08.png

<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的变种:

l09.png

<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项主轴尺寸的差异:

l10.png

<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容器在主轴尺寸的差异

l11.png

<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


文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐