重新学习 flex-grow、flex-shrink、flex-basis

2,262 阅读4分钟

本文要点:

  • flex 布局中,flex 设置单、双、三值的时候分别有什么意义。
  • flex 布局怎么压缩负空间(flex-shrink)
  • flex 布局怎么分配多余的空间(flex-grow)
  • flex-basis 和 flex-grow 怎么配合分配空间

例子

先定义结构:

<div class="list">
  <div class="item singleValue">先帝创业未半</div>
  <div class="item singleValue">而中道崩殂</div>
  <div class="item singleValue">今天下三分</div>
</div>

<div class="list">
  <div class="item doubleValue">先帝创业未半</div>
  <div class="item doubleValue">而中道崩殂</div>
  <div class="item doubleValue">今天下三分</div>
</div>

基础样式:

.list {
  display: flex;
  box-sizing: border-box;
  width: 300px;
  margin: 10px;
}

.item {
  background: pink;
}
.item:nth-child(2) {
  background: lightblue;
}

flex 单值

无单位的时候,flex 设置了单值,相当于 flex-grow ,表示子项的弹性比例:

.singleValue {
  /* 单值无单位相当于 flex-grow ,定义弹性盒子项(flex item)的拉伸因子 */
  flex: 1;
}
.singleValue:first-child {
 /* 等同于 flex: 2 */
  flex-grow: 2;
}

效果:

.singleValue:last-child {
/*  单值带单位,相当于 flex-basis  */
/*   flex: 75px; */
  flex-basis: 75px;
}

效果:

我发现第三个子项并不是期待的 75px ,这个疑问稍后再讲。

双值或者三值

双值第一个为 flex-grow , 第二个如果有单位,是 flex-basis

.doubleValue {
  flex: 1 300px;
}

List 总宽度 300 ,每个子项 300px ,远远超出了 flex 弹性盒子的范围,因此会根据 flex 的算法自动调整子项宽度如下:

此时设置三值:

.doubleValue:first-child {
  flex: 1 0 300px;
}

等同于:

.doubleValue:first-child {
 flex-shrink: 0; 
}

也就是说,双值 第一个 flex-grow ,第二个无单位的话,是 flex-shrink 。 三值的话,分别是 flex-grow | flex-shrink | flex-basis.

效果如下:

flex-shrink 的计算方式

当我们给子项设置的宽度之和大于父元素的宽度时,flex-shrink 就派上用场了。

默认情况下,每个子项的 flex-shrink 的值是 1. 所有子项会等比例自动压缩,省心省力。

实际项目中比较常用的 flex-shrink 的值,可能会是 0 或者 1,默认是 1 ,不想让子项被自动压缩空间的话,设为 0 就好了。

而在 flex-shrink 的官方定义里,我发现 flex-shrink 的值可以是 0 到无穷大,即非负值。那么 flex-shrink 怎么计算压缩的值呢?

我把上边双值 class 改为:

.doubleValue {
  flex-basis: 150px;
}

.doubleValue:first-child {
  flex-basis: 300px;
  flex-shrink: 2;
}

得到了三个 100px 宽的子项:

drafts.csswg.org/css-flexbox… 里对 flex-shrink 压缩因子的定义是:

…which determines how much the flex item will shrink relative to the rest of the flex items

似懂非懂。实际上我们使用 flex 布局时并不需要了解 flex-shrink 是怎么分配空间的,浏览器为我们准备好了一切。

我在 Day 24: flex-shrink calculation | SamanthaMing.com 里找到一个算法:

验证下:

计算 shrunk space:

Shrunk space = (300 +150 + 150) - 300 = 300

有 300px 的多余空间需要压缩。

计算 shrink ratio

total shrink scaled width = 300 * 2 + 150 * 1 + 150 * 1 = 900

first child Shrink ratio = 300 * 2 / 900 = 0.666…

Nth-child(2/3) shrink ratio = 150 * 1 / 900 = 0.166…

计算新的宽度

first child new width = 300[flex-basis value] - (300 [shrunk space]* 0.666..)= 100px

Nth-child(2/3) new width = 150 - 300 * 0.166… = 100px

因此才会有被等分的三个子项:

例外情况

我想让子项好看点儿,于是加了 padding :

.item { padding: 10px; background: pink; }

看看我得到了什么:

改为:

.item {
  box-sizing: border-box;
  padding: 10px;
  background: pink;
}

上述公式有效。

flex-grow

子项宽度之和小于父元素宽度时,flex-grow 的设置就很有必要了。

依然借用 www.samanthaming.com/flexbox30/2… 里的一个图来说明 flex-grow 如何分配剩余空间:

.doubleValue {
  flex-grow: 1;
  flex-basis: 50px;
}

.doubleValue:first-child {
  flex-grow: 2;
  flex-basis: 100px;
}

我得到了 150px、75px、75px:

需要被分配的剩余空间 = 300 - (100 + 50 + 50) = 100px

Flex-grow 总份数 = 2 + 1 + 1 = 4

First-child 可以被分配的多余空间 = 100 * 2/4

其它子项可以被分配的多余空间 = 50 * 1/4

first-child 的最终宽度: 100 + 100 * 2/4 = 150px

其它子项最终宽度:50 + 100*1/4 = 75px

回头再看看之前的 flex-basis 设置

.singleValue {
  flex: 1;
}
.singleValue:first-child {
  flex-grow: 2;
}
.singleValue:last-child {
  flex-basis: 75px;
}

分别得到:112.5px、56.25px、131.25px:

前两个子项按比例分配,第三个在按比例的基础上增加了 flex-basis 的设置,于是得到了 flex-grow 为 1 时自动分配的空间 + flex-basis 的总和。

接着改为:

.singleValue {
  flex: 1;
  flex-basis: 10px;
}
.singleValue:first-child {
  flex-grow: 2;
}
.singleValue:last-child {
  flex-basis: 20px;
}

现在,三个子项的宽度分别是:140px、75px、85px 。

回头看 flex-basis 的定义:

flex-basis 指定了 flex 元素在主轴方向上的初始大小。如果不使用 box-sizing 改变盒模型的话,那么这个属性就决定了 flex 元素的内容盒(content-box)的尺寸。

flex-basis 的默认值是 auto .