当前位置: 首页 > Web前端 > CSS

flex布局元素如何分配容器的剩余空间

时间:2023-03-31 01:23:21 CSS

自从开始学习CSS布局以来,能更灵活地将父元素的空间分配给各个子元素,一直是每个前端程序员的梦想。在flex之前,如果不专门搜索相关解决方案,一般人很难想出一个非常灵活的三(多)列等高布局方案,即使看了解决方案,也有很多人会称之为一个恶作剧。不得不感慨,flex之前的CSS布局功能很弱:基本上只能通过一些不是为layout设计的属性来实现想要的layout-float,inline-block,position,甚至table。使用这些属性来实现各种布局效果,往往会遇到不少其他的坑:比如浮动闭包,inline-block垂直对齐,position定位原点,table不灵活等等。直到flexflex的出现,可以说是解决了一次解决了前端布局的所有问题(当然还没有完全解决,不然就没有网格布局了),之前很难实现的布局效果在flex下简直不能更简单,以至于其他一些平台已经开始吸收flex布局思想,一些开源项目将flex布局方式移植到其他平台。中文社区也有很多关于flex的文章,比如ruanyifeng。不过我个人觉得很多关于flex的文章都有一个通病,就是flex容器,flex项,mainaxis(主轴),corsaxis(横轴)等一堆flex相关的名词,不能'不禁让人望而生畏,一直没弄明白是怎么回事,就来了一堆术语。然而,这还不是最大的问题。最大的问题是很多文章都没有解释flex布局的详细计算方法,尤其是ruanyifeng的文章。文章将出现在第一页。因为我觉得他文笔不好,所以就不贴地址了。想看的同学可以自行搜索,在首页。连MDN和《The Book Of CSS3》都没有说清楚flex-grow和flex-shrink是怎么计算的。所以我决定写这篇文章来解释一下flex-grow和flex-shrink的详细计算方法。flex如何解决传统的常见布局问题?传统布局中最常见也是最迫切的需求当然是将父元素的空间从左到右分配给子元素,实现多列布局:无论是按比例、固定宽度,还是更灵活的固定宽度加占位剩余空间也不错。那我们就从如何使用flex实现三栏布局说起吧。如果要实现三列等高的布局,两侧边栏宽度固定,中间列占据剩余空间,如下代码即可:

section元素的宽度将与block元素一样宽,对于外部来说它表现得像一个块元素。这三个元素会从左到右(很明显)占据父元素的空间。左右侧边栏的宽度都是200px,中间的.content元素的宽度会占据section元素剩余的宽度。另外,section的高度会自动被最高的子元素拉高,其他子元素的高度也会被拉到和section元素一样的高度,如果设置了section元素的高度,设置所有子元素的高度为auto,所有子元素会自动和父元素一样高,这简直是传统布局中的梦想功能!总而言之,flex在高度方面的表现非常直观。此外,如果您不为flex子项设置width和flex-grow,它将尝试尽可能窄。flex-grow的计算上面的demo中最值得注意的是.content元素的flex-grow属性,可以设置为1来填满剩余的水平空间。这也是本文的重点:讲解flex-grow和flex-shrink属性的详细计算方法。flex-grow属性决定了当父元素在空间分配方向上有剩余空间时如何分配剩余空间。它的值是一个权重(也叫扩展因子),默认为0(纯值,无单位),剩余空间会按照这个权重进行分配。比如剩余空间为x,三个元素的flex-grow分别为a,b,c。设总和为a+b+c。那么这三个元素会分别得到剩余空间xa/sum,xb/sum,x*c/sum,也是为了权重。例如:父元素的宽度为500px,三个子元素的宽度分别为100px、150px、100px。所以剩余空间为150px,三个元素的flex-grow分别为1、2、3,所以和为6,三个元素得到的额外空间为:150*1/6=25px150*2/6=50px150*3/6=75px三个元素最终的宽度分别为125px、200px、175px。100px+25px=125px150px+50px=200px100px+75px=175px你可以打开这个demo(下面的demo都在这个页面)用开发工具查看。注意不要用截图工具测量,可能会不准确,因为高分屏、放大倍率等很多因素都会影响测量结果。然而!不仅如此,还有一种情况:当所有元素的flex-grow之和小于1时(注意是1,也就是说每个元素的flex-grow都是小数比如0.2),上面公式中的和都会用1来参与计算,不管他们的和。也就是说,当所有元素的flex-grow之和小于1时,剩余的空间不会分配给每个元素。实际上分配使用的空间是sum(flex-grow)/1*剩余空间,分配使用的空间还是按照flex-grow的比例分配的。还是上面的例子,但是三个元素的flex-grow分别是0.1,0.2,0.3,那么计算公式就会变成:150*0.1/1=15px150*0.2/1=30px150*0.3/1=45px150px-15px-30px-45px=60px,即还有60px没有分配给任何子元素。三个元素最终的宽度为:100px+15px=115px150px+30px=180px100px+45px=145px上面说了flex-grow是这样计算的。另外flex-grow也会受到max-width的影响。如果最终的增长结果大于max-width指定的值,则优先使用max-width的值。它还会导致父元素的部分剩余空间未分配。flex-shrink的计算方法上面说了flex几乎一次性解决了前端布局的所有问题。所以既然有多余空间的时候可以把多余的空间分配给各个子元素,当然空间不够的时候也可以收缩各个子元素来适应有限的空间。这就是flex-shrink属性的作用。你可能认为flex-shrink的计算方式与flex-grow类似,但事情并没有那么简单。flex-shrink属性定义了当空间不足时单个元素如何收缩。它的值默认为1,很多文章基本都是这么说的:“flex-shrink属性定义了一个元素的收缩系数”,而没有提到它是如何计算的。flex-shrink定义的只是一个减少元素宽度的权重组件。每个元素收缩多少还有另一个重要因素,那就是它自己的宽度。例如:父元素500px。三个子元素分别设置为150px、200px、300px。三个子元素的flex-shrink值分别为1、2、3。首先计算子溢出多少:150+200+300-500=-150px。然后-150px会通过将三个元素缩小一定量来补偿。具体计算方法是:每个元素的收缩重量是它的flex-shrink乘以它的width。所以总重量为1150+2200+3*300=1450。三个元素分别收缩:1501(flex-shrink)150(width)/1450=-15.51502(flex-shrink)200(width)/1450=-41.41503(flex-shrink)300(width)/1450=-93.1三个元素的最终宽度为:150-15.5=134.5200-41.4=158.6300-93.1=206.9同样,当flex-shrinkofallelements当它小于1时,计算方式会有所不同:此时不会收缩所有的空间,只会收缩flex-shrink之和与1之比的空间。还是上面的例子,只是flex-shrink分别改成了0.1,0.2,0.3。所以总重量为145(正好小了10倍,省略计算公式)。三个元素的总收缩量不是150px,而是只有150px(0.1+0.2+0.3)/1,也就是60%的空间:90px。每个元素的收缩空间为:900.1(flex-shrink)150(width)/145=9.31900.2(flex-shrink)200(width)/145=24.83900.3(flex-shrink)300(width)/145=55.86三个元素最终的宽度为:150-9.31=140.69200-24.83=175.17300-55.86=244.14当然,和flex-grow类似,flex-shrink也会受到min-width的影响。总结虽然上面的公式看起来很复杂,但计算过程其实比较简单:如果所有元素的flex-grow/shrink之和大于等于1,则调整所有子元素的尺寸以适应尺寸父元素(不考虑max/min-width/height),如果flex-grow/shrink之和小于1,则只会按照所有元素的flex-grow/shrink之和的比例进行增长或收缩相对于1。growing时每个元素的权重为该元素的flex-grow的值;收缩时每个元素的权重是元素的flex-shrink乘以其宽度的值。