[css3] 深入理解 css3 中的 flex-grow、flex-shrink、flex-basic

深入理解 css3 中的 flex-grow、flex-shrink、flex-basic

原文链接: 深入理解css3中的flex-grow、flex-shrink、flex-basis

flex为 css 的布局带来了新的时代,作为一个重构工程师,我们再也不用局限于 float 和 position,特别是在移动端,我们可以利用 flex 轻松实现以往 float 和 position 很难实现甚至是无法实现的布局。本文主要讲解 flex 的三个子属性:flex-grow、flex-shrink、flex-basis。他们只是博大精深的 flex 中的一部分,本文默认你对 flex 已经有初步的了解,如果不了解,建议选看看这里:

flex 布局发生在父容器和子容器之间。父容器需要有 flex 的环境display: flex;,子容器才能根据自身的属性来布局,简单的说,就是瓜分父容器的空间。相反就是说如果父容器没有 flex 的环境,那么子容器就无法使用 flex 的规则来划分父容器的空间。

讲到瓜分父容器的空间,那么首先需要讲一个很重要的词:剩余空间
什么是剩余空间呢?具备 flex 环境的父窗口,通常是有一条主轴和一条侧轴,默认情况下主轴就是水平从左向右的,侧轴是垂直从上到下的(类似书写模式)。剩余空间是父容器在主轴的方向上还有多少可用的空间。比如看下面这段 html 结构:

"container"> "B1"> "B2"> "B3">

container 就是父容器,B1 B2 B3 就是子容器,假如 container 的 width 是 100px,那么剩余空间就是: 500px - B1.width - B2.width - B3.width。嗯就是这么简单!

flex-grow

flex-grow (default: 0)知道了剩余空间的概念,首先来看一下 flex-grow。上面那个例子,我们假设 container 的 width 是 500px,现在我们再假设 B1、B2、B3的 width 是 100px,那么剩余空间就是 500 - 100 * 3 = 200。知道了剩余空间有什么用呢?这个时候 flex-grow 就该出场了,假如我们这个时候对 B1设置 flex-grow: 1,那么我们会发现,B1把 B2 和 B3 都挤到右边了,也就是说剩余的 200px 空间都被 B1 占据了,所以此时 B1 的 width 比实际设置的值要大。

<style type="text/css">
.wrap {
  border: 1px solid red;
  height: 500px;
  display: flex;
  width: 500px;
}

.wrap .B {
  width: 100px;
  height: 100px;
  box-sizing: border-box;
}

.B1 {
  background-color:rgba(255,255,0,.5);
  flex-grow: 1;
}

.B2{
    background-color:rgba(255,0,255,.5);
}
.B3{
    background-color:rgba(0,255,255,.5);
}

style>
<div class="wrap">
  <div class="B B1">B1div>
  <div class="B B2">B2div>
  <div class="B B3">B3div>
div>

[css3] 深入理解 css3 中的 flex-grow、flex-shrink、flex-basic_第1张图片
在 jsrun 中打开示例

所以这里 flex-grow 的意思已经很明显了,就是索取父容器的剩余空间,默认值是 0,就是三个子容器都不索取剩余空间。但是当 B1 设置为 1的时候,剩余空间就会被分成一份,然后都给了 B1。如果此时 B2设置了 flex-grow: 2,那么说明 B2 也参与到瓜分剩余空间中来,并且他是占据了剩余空间中的 2 份,那么此时父容器就会把剩余空间分成 3 份,然后 1 份给到 B1,2份给到 B2,如下面这样子。
B1的宽度为:100 + 200 / 3 * 1
B2 的宽度为:100 + 200 / 3 * 2
B3的宽度为:100

<style type="text/css">
.wrap {
  border: 1px solid red;
  height: 500px;
  display: flex;
  width: 500px;
}

.wrap .B {
  width: 100px;
  height: 100px;
  box-sizing: border-box;
}

.B1 {
  background-color:rgba(255,255,0,.5);
  flex-grow: 1;
}

.B2{
    background-color:rgba(255,0,255,.5);
  flex-grow: 2;
}
.B3{
    background-color:rgba(0,255,255,.5);
}

style>
<div class="wrap">
  <div class="B B1">B1div>
  <div class="B B2">B2div>
  <div class="B B3">B3div>
div>

flex-basis

flex-basis (default:auto) 初次见 flex-basis 这个属性,还挺疑惑的,不知道它是用来干嘛的。后来研究发现,这个属性值的作用也就是 width 的替代品。如果子容器设置了 flex-basis 或者 width,那么在分配空间之间,他们会先跟父容器预约这么多的空间,然后剩下的才是归入的剩余空间,然后父容器再把剩余空间分配给设置了 flex-grow 的容器。如果同时设置 flex-basis 和 width,那么 width 属性会被覆盖,也就是说 flex-basis 的优先级比 width 高。有一点需要注意,如果 flex-basis 和 width 其中有一个是 auto,那么另外一个非auto 的属性优先级会更高。

<div class="wrap">
  <div class="B B1">B1div>
  <div class="B B2">B2div>
  <div class="B B3">B3div>
div>
.wrap {
  border: 1px solid red;
  height: 500px;
  display: flex;
  width: 500px;
}

.wrap .B {
  width: 100px;
  height: 100px;
  box-sizing: border-box;
}

.B1 {
  background-color:rgba(255,255,0,.5);
  flex-grow: 1;
}

.B2{
    background-color:rgba(255,0,255,.5);
    flex-basis: 150px;
}
.B3{
    background-color:rgba(0,255,255,.5);
}

[css3] 深入理解 css3 中的 flex-grow、flex-shrink、flex-basic_第2张图片
在 jsrun 中打开示例

tips: flex-basis 和 width 为 auto 值,那最后的空间就是根据内容多少来定的,内容多占据的水平空间就多。

<div class="wrap">
  <div class="B B1">B1div>
  <div class="B B2">B2div>
  <div class="B B3">B3div>
div>
.wrap {
  border: 1px solid red;
  height: 500px;
  display: flex;
  width: 500px;
}

.wrap .B {
  height: 100px;
}

.B1 {
  background-color:rgba(255,255,0,.5);
  width: 100px;
  flex-grow: 1;
}

.B2{
    background-color:rgba(255,0,255,.5);
  flex-basis: auto;

}
.B3{
    background-color:rgba(0,255,255,.5);
  width: auto;
}

[css3] 深入理解 css3 中的 flex-grow、flex-shrink、flex-basic_第3张图片
在 jsrun 中打开示例

flex-shrink

flex-shrink(default: 1)好了, 上面讲了这么多,你们应该都明白了把。有人会想,不就这样嘛,很容易啊,不就是剩余空间的分配吗?

是的,上面讲的都是剩余空间的分配。但是,你有没有想过还有没有其他的情况呢?可以发现, 上面讲的例子 B1 B2 B3 的宽度总和都是没有超过父容器的宽度的。那如果三个子容器的宽度和超过父容器的宽度呢?那还有剩余空间可以分配吗?此时浏览器又是怎么处理呢?请看下面:

tips: flex 环境默认是不换行的,即使父容器宽度不够也不会,除非设置 flex-wrap 来换行

<div class="wrap">
  <div class="B B1">B1div>
  <div class="B B2">B2div>
  <div class="B B3">B3div>
div>
.wrap {
  border: 1px solid red;
  height: 500px;
  display: flex;
  width: 500px;
}

.wrap .B {
  height: 100px;
  box-sizing: border-box;
}

.B1 {
  background-color:rgba(255,255,0,.5);
  flex-grow: 1;
  width: 300px;
}

.B2{
    background-color:rgba(255,0,255,.5);
  width: 160px;
}
.B3{
    background-color:rgba(0,255,255,.5);
  width: 120px;
}

[css3] 深入理解 css3 中的 flex-grow、flex-shrink、flex-basic_第4张图片
在 jsrun 中打开示例

此时我们会发现,B1 设置的 flex-grow 没有作用,不但没有获取到剩余空间,他的空间甚至是比他定义的 300px 还要小,而且我们发现 B2 和 B3 的空间也相应的被压缩了。那么这里的问题是:

  • 为什么 flex-grow 没有作用,反而被压缩呢?
  • 三个容器的压缩比例是怎样的呢?

这就是这一节的重点了flex-shrink

同样的,三个容器处于 flex 环境中,所以布局之前,父容器还是会计算剩余空间。这一次计算的结果是这样的:剩余空间 = 500px - 300px - 160px - 120px = -80px,剩余空间是一个负数所以很容易理解第一个问题,即使是设置了 flex-grow,但
是由于没有剩余空间,所以 B1 分配到的空间是0。

由于 flex 环境的父容器的宽度是 500px 是不会变,所以为了是子容器的宽度和最多为父容器的宽度,那就只有两个办法:

  • 第一个是使子容器换行
  • 第二个是压缩子容器使之刚好撑满父容器的宽度。

因为 flex 子容器是默认不换行的,所以这里不做讨论。而第二种压缩,实际上就是上面例子表现出来的样式。现在就遇到了上面第二个问题,这三个的压缩比例是多少呢,各自需要压缩的空间是多少呢?

这个时候就需要谈谈 flex-shrink,这个属性其实就是定义了一个子容器的压缩比例。他的默认值是 1,所以上面那个例子,就是三个子容器压缩的比例是一样的: 1: 1: 1。如果此时我们设置 B1 的压缩比例是 2 那会怎样呢?

<div class="wrap">
  <div class="B B1">B1div>
  <div class="B B2">B2div>
  <div class="B B3">B3div>
div>
.wrap {
  border: 1px solid red;
  height: 500px;
  display: flex;
  width: 500px;
}

.wrap .B {
  height: 100px;
  box-sizing: border-box;
}

.B1 {
  background-color:rgba(255,255,0,.5);
  flex-shrink: 2;
  width: 300px;
}

.B2{
    background-color:rgba(255,0,255,.5);
  width: 160px;
}
.B3{
    background-color:rgba(0,255,255,.5);
  width: 120px;
}

[css3] 深入理解 css3 中的 flex-grow、flex-shrink、flex-basic_第5张图片
在 jsrun 中打开示例

设「压缩率」为 x,则 300(1 - 2x) + 160(1 - x) + 120(1 - x) = 500
x = 1 / 11
B1的宽度为 300 (1 - 2 /11 ) = 245.45

总结

通过上面的分析, 我们可以得出这样几个结论:

  • 剩余空间 = 父容器空间-子容器1.flex-basis/width - 子容器2.flex-basis/width - …
  • 如果父容器空间不够,就走压缩 flex-shrink,否则走扩张 flex-grow;
  • 如果你希望某个容器在任何时候都不被压缩,那设置 flex-shrink: 0;
  • 如果子容器的 flex-basis 设置为 0(width 也可以,不过 flex-basis 更符合语义),那么计算剩余空间的时候将不会为子容器预留空间。
  • 如果子容器的 flex-basis 设置为 auto(width 也可以,不过 flex-basis 更符合语义),那么计算剩余空间的时候将会根据子容器内容的多少来预留空间。

你可能感兴趣的:(css)