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

所属分类: 网页制作 / CSS 阅读数: 1793
收藏 0 赞 0 分享

自从开始开学习 CSS 布局,想要比较灵活的把父元素的空间分配给各个子元一直是各个前端程序员的梦想。

在 flex 之前,如果不是专门去搜索相关的解决方案,一般人几乎想不出非常灵活的三(多)栏等高布局方案,而即使看了解决方案,很多人也会大呼奇技淫巧。

不得不感慨在 flex 之前 CSS 的布局功能之弱:基本只能使用一些并非为布局而设计的属性来实现想要的布局——float、inline-block、position、甚至是 table 等。而使用这些属性来实现各种布局效果,往往又会遇到相当多另外的坑:比如浮动的闭合、inline-block 的垂直对齐、position 的定位原点以及 table 的不够灵活等。

直到出现了 flex

flex 可以说是一次性解决了前端布局的所有的问题(当然,并没有完全解决,要不然也不会有 grid layout 了),以前很难实现的布局效果在 flex 下简直不能更简单,以至于一些其它平台也开始吸纳 flex 的布局思想,也有些开源项目把 flex 的布局方式移植到其它平台。

中文社区也有不少写 flex 的文章,比如 ruanyifeng。然而个人觉得不少写 flex 的文章都有个通病,就是一上来就整一堆 flex 相关的术语,比如 flex container,flex item,main axis(主轴),cors axis(交叉轴),不禁让人望而生畏,都还没搞清楚怎么回事,就来一堆术语。

然而这还不是最大的问题,最大的问题是很多文章并没有把 flex 布局的详细计算方式讲清楚,尤其是连 ruanyifeng 的文章也没把这事说清楚,但是在 Google 搜索 flex 相关的文章,他的文章却会出现在第一页。因为我觉得他写的并不好,所以就不贴地址了,想看的同学可以自己搜一下,就在第一页。

即使是 MDN 以及《The Book Of CSS3》里也没把 flex-grow 和 flex-shrink 的计算方式说清楚。

所以我决定写这一篇文章,把 flex-grow 与 flex-shrink 的详细计算方式讲清楚。

flex 如何解决传统常见布局问题

在传统布局中最常见也是急需的当然就是在从左往右把父元素的空间分配给子元素以实现多栏布局了:按比例也好,定宽也好,更灵活的定宽加占用剩余空间也好。

那我们就从使用 flex 如何实现三栏布局开始吧。

想要实现三栏等高布局,且两边的侧栏宽度固定而中间一栏占用剩余的空间,如下代码就足够了:

<style>
  section {display: flex;}
  .left-side,
  .right-side {width: 200px;}
  .content {flex-grow: 1;}
</style>
<section>
  <div class="left-side"></div>
  <div class="content"></div>
  <div class="right-side"></div>
</section>

其中 section 元素的宽度将会像 block 元素一样尽量的宽,对外面的元素来说,它的行为很像一个 block 块。三个元素会从左往右占据父元素的空间(这很显然)。左右侧边栏的宽度都是 200px,中间 .content 元素的宽度将会占据 section 元素的剩余宽度。

另外,section 的高度会自动被最高的一个子元素撑开,同时其它子元素的高度也会被拉到跟 section 元素一样高,而如果给 section 元素设置了高度,而所有子元素的高度设置为 auto ,所有的子元素也都会自动跟父元素一样高,这简直就是在传统布局中做梦都想要的功能!

总之,在高度方面,flex 的表现是相当符合直觉的。

另外,如果不给 flex 子元素设置宽度和 flex-grow,它会尽量的窄。

flex-grow 的计算方式

上面 demo 中最值得注意的是 .content 元素的 flex-grow 属性,设置为 1 它就可以占满水平剩余空间。这也是本文的重点:讲清 flex-grow 与 flex-shrink 属性的详细计算方式。

flex-grow 属性决定了父元素在空间分配方向上还有剩余空间时,如何分配这些剩余空间。其值为一个权重(也称扩张因子),默认为 0(纯数值,无单位),剩余空间将会按照这个权重来分配。

比如剩余空间为 x,三个元素的 flex-grow 分别为 a,b,c。设 sum 为 a + b + c。那么三个元素将得到剩余空间分别是 x a / sum, x b / sum, x * c / sum,是为权重也。

举个例子:

父元素宽度 500px,三个子元素的 width 分别为 100px,150px,100px。

于是剩余空间为 150px

三个元素的 flex-grow 分别是 1,2,3,于是 sum 为 6
则三个元素所得到的多余空间分别是:

150 * 1 / 6 = 25px
150 * 2 / 6 = 50px
150 * 3 / 6 = 75px
三个元素最终的宽度分别为 125px,200px,175px。

100px + 25px = 125px
150px + 50px = 200px
100px + 75px = 175px
可以打开这个 demo(下文中所有的 demo 都在这个页面) 然后用开发工具查看一下。注意不要用截图工具量,可能量不准,因为高分屏和放大等诸多因素都会影响测量结果。

然而!不止这些,还有一种情况:

当所有元素的 flex-grow 之和小于 1 的时候(注意是 1,也就是说每个元素的 flex-grow 都是一个小数如 0.2 这样的),上面式子中的 sum 将会使用 1 来参与计算,而不论它们的和是多少。也就是说,当所有的元素的 flex-grow 之和小于 1 的时候,剩余空间不会全部分配给各个元素。

实际上用来分配的空间是 sum(flex-grow) / 1 * 剩余空间,这些用来分配的空间依然是按 flex-grow 的比例来分配。

还是上面一个例子,但是三个元素的 flex-grow 分别是 0.1,0.2,0.3,那么计算公式将变成下面这样:

150 * 0.1 / 1 = 15px
150 * 0.2 / 1 = 30px
150 * 0.3 / 1 = 45px
150px - 15px - 30px - 45px = 60px,即还有 60px 没有分配给任何子元素。

三个元素的最终宽度分别为:

100px + 15px = 115px
150px + 30px = 180px
100px + 45px = 145px

如上所述即是 flex-grow 的计算方式。

另外,flex-grow 还会受到 max-width 的影响。如果最终 grow 后的结果大于 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 乘以其宽度。

所以总权重为 1 150 + 2 200 + 3 * 300 = 1450

三个元素分别收缩:

150 1(flex-shrink) 150(width) / 1450 = -15.5
150 2(flex-shrink) 200(width) / 1450 = -41.4
150 3(flex-shrink) 300(width) / 1450 = -93.1

三个元素的最终宽度分别为:

150 - 15.5 = 134.5
200 - 41.4 = 158.6
300 - 93.1 = 206.9

同样,当所有元素的 flex-shrink 之和小于 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。

每个元素收缩的空间为:

90 0.1(flex-shrink) 150(width) / 145 = 9.31
90 0.2(flex-shrink) 200(width) / 145 = 24.83
90 0.3(flex-shrink) 300(width) / 145 = 55.86

三个元素的最终宽度分别为:

150 - 9.31 = 140.69
200 - 24.83 = 175.17
300 - 55.86 = 244.14

当然,类似 flex-grow,flex-shrink 也会受到 min-width 的影响。

虽然上面的公式看起来很复杂,其实计算过程还是比较简单的:如果所有元素的 flex-grow/shrink 之和大于等于 1,则所有子元素的尺寸一定会被调整到适应父元素的尺寸(在不考虑 max/min-width/height 的前提下),而如果 flex-grow/shrink 之和小于 1,则只会 grow 或 shrink 所有元素 flex-grow/shrink 之和相对于 1 的比例。grow 时的每个元素的权重即为元素的 flex-grow 的值;shrink 时每个元素的权重则为元素 flex-shrink 乘以 width 后的值。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

更多精彩内容其他人还在看

css实现的让图片垂直居中的方法

图片的宽度和高度是未知的,没有一个固定的尺寸,在这个前提下要使图片在一个固定了宽度和高度的容器中垂直居中,想想感觉还是挺麻烦的,由于最近的项目可能会用到这个方案,所以把一些常用的方法都收集整理了一下。
收藏 0 赞 0 分享

3个比较有用的网页制作技巧

我们在进行DivCSS布局的时候,非常关注CSS技巧的学习,今天向大家推荐3个所谓的 “顶级”CSS技巧!虽然没有什么新意,但对新手而言却非常重要!
收藏 0 赞 0 分享

让你写出更轻巧、更快并且更不会让你头疼的CSS的方法

为什么我们的CSS变得一团糟——我们真的很容易陷入这样的困惑中。有时这是一开始就马虎编程的结果,有时是由于后期多重的附加代码(hacks)和修改造成的。
收藏 0 赞 0 分享

利用CSS3的checked伪类实现OL的隐藏显示的方法

随着CSS3的发布,国外研究正如火如荼,但在国内还有很多人抱着IE不支持CSS3的想法,始终无动于衷不肯去学习。
收藏 0 赞 0 分享

CSS多浏览器兼容性(IE和Firefox)技巧大全推荐

CSS对浏览器的兼容性有时让人很头疼,或许当你了解当中的技巧跟原理,就会觉得也不是难事,从网上收集了IE7,6与Fireofx的兼容性处理技巧并整理了一下。
收藏 0 赞 0 分享

固定位置显示弹出层(兼容IE6,IE8,FF)

兼容IE6,IE8,FF弹出层固定位置显示,主要是通过css的控制
收藏 0 赞 0 分享

div完美自适应动态上下左右居中

div完美自适应动态上下左右居中,多用于信息提示框效果。
收藏 0 赞 0 分享

display:inline的用法

和 display:inline 对应的是 display:block,block 会让应用了该 CSS 属性的 HTML 标记变成块级别元素,例如 SPAN 是行内显示的,但是你加了 display:block 属性就不一样了
收藏 0 赞 0 分享

css样式实现整个页面背景使用一张图片

最近一直被css背景困扰,因为是仿站,别人网站背景图片无论怎样另存,都是相同文件名,打开一看,整个网站的各个角落背景图片都在一个文件中分布,于是上网搜集资料,发现使用这种方法的网站很多,源自于雅虎最早。
收藏 0 赞 0 分享

关于CSS样式表文件组织形式的整理

以下引用网络上的一篇文章,不知道作者是谁了,总体上概括的不错,但是在实际应用当中还是要根据项目情况来具体实施比较好。
收藏 0 赞 0 分享
查看更多