浅析 flex-grow、flex-shrink、flex-basis

1,708 阅读3分钟

Flexbox 支持对元素大小的灵活控制。本文重点说说 flex-basisflex-shrinkflex-grow 的算法。

flex-basis

指定了 flex 元素在主轴方向上的初始大小。默认值是 auto,关于 auto 这个属性值 MDN 上还有一段简史,可以了解下。直接上:chestnut:

<div class="container">
  <div class="item one">200px</div>
  <div class="item two">500px</div>
</div>
.container {
  display: flex;
  width: 1000px;
  height: 50px;
  background-color: green;
}
.item {
  flex-basis: auto;
  height: 100%;
  text-align: center;
  line-height: 50px;
}
.one {
  width: 200px;
  background-color: red;
}
.two {
  width: 500px;
  background-color: orange;
}
WX20190424-201645@2x

关键字 auto 的意思好像是把 widthheight 设置为自动,实际上并不是这么一回事。auto 的意思是这个项目可以从对应的属性(widthheight)那里获得主尺寸,如果没有设置主尺寸,根据内容确定大小——类似于行内块或者浮动元素!

flex-basis 还能设置成 content 值,意思是根据项目内容确定大小——跟 auto 的区别在于忽略 widthheight 设置的主尺寸。浏览器对 content 关键字支持度不一致!!

flex: 0 会是什么效果呢?

WX20190424-202456@2x

可以看到,子元素的 width 都失效了。其实它是子元素的最小宽度(隐形最小宽度——即便你通过属性指定可伸缩项要收缩,但它们可能也不会收缩到可容纳内容的大小之下。)。将 500px 替换成一个 DOM:

<div class="item two">
  <div class="two-child">400px</div>
</div>
.two-child {
  width: 400px;
}

结果如下图所示:

WX20190424-204007@2x

flex-grow

属性表示剩余空间的一个比值,默认值是0。回到最初 200px500px 的例子,200 + 500 = 700 并没有完全撑满整个父元素 .container 的宽度,剩余的宽度是如何处理的呢?

.one {
  flex-grow: 1;
}
.two {
  flex-grow: 1;
}

通过上述样式,就可以将剩余的 300px 平均分给.one .two元素。

WX20190424-205440@2x

如果结合下面样式会是怎样的呢?

.item {
  flex-basis: 0%;
}

答案是 .one .two 平分1000px,即各 500px。不受子元素的影响。

WX20190424-210237@2x

flex-shrink

父元素宽度不够时就要压缩子元素的宽度,通过 flex-shrink 配置。缩小不比扩大那么简单,缩小的计算方法会复杂一些。复杂的根源在于,再大的子元素也不能导致小元素压缩得不见了。还是用最前面的例子:

.one {
  width: 600px;
  flex-shrink: 1;
}
.two {
  width: 800px;
  flex-shrink: 1;
}

按照自动放大的方式,应该是 (600+800-1000)/2, .one 应该是 400px.two 应该是600px

WX20190424-211401@2x

结果 .one428.58px,这是如何计算过来的呢?

.one = (600 * 1/(600*1 + 800*1))*1000428.58
.two = (800 * 1/(600*1 + 800*1))*1000571.42

我们换一个比例来验证一下想法:

.one {
  width: 600px;
  flex-shrink: 2;
}
.two {
  width: 800px;
  flex-shrink: 3;
}

通过计算我们得到:

.one = (600*2/(600*2 + 800*3))*400133.33
.two = (800*3/(600*2 + 800*3))*400266.67

所以 .one 的宽度是 600 - 133.33 px, .two 的宽度是 800 - 266.67 px。下面验证一下结果:

屏幕快照 2019-04-25 22 24 18 屏幕快照 2019-04-25 22 25 27

Bingo!正如我们的猜想那样。这里用 A 表示第一个子元素,B 表示第二个子元素。计算公式如下:

A 压缩的大小 = (A.flex-shrink * A.flex-basis) / (A.flex-shrink * A.flex-basis + B.flex-shrink * B.flex-basis) * 总体超出的宽度
B 压缩的大小 = (B.flex-shrink * B.flex-basis) / (A.flex-shrink * A.flex-basis + B.flex-shrink * B.flex-basis) * 总体超出的宽度

flex

flexflex-growflex-shrinkflex-basis 的缩写,当 flex-basis 是 0 的时候一定/必须/务必要带上单位,0px 或 0% 任君选择。

留一道思考题,上述最后一个例子,如果给 .one 一个 min-width: 500px; 计算会有什么不同吗?

欢迎指点缺陷,更多好文前往博客