原文链接: 深入理解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 (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>
所以这里 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 (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);
}
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;
}
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;
}
此时我们会发现,B1 设置的 flex-grow 没有作用,不但没有获取到剩余空间,他的空间甚至是比他定义的 300px 还要小,而且我们发现 B2 和 B3 的空间也相应的被压缩了。那么这里的问题是:
这就是这一节的重点了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;
}
设「压缩率」为 x,则 300(1 - 2x) + 160(1 - x) + 120(1 - x) = 500
x = 1 / 11
B1的宽度为 300 (1 - 2 /11 ) = 245.45
通过上面的分析, 我们可以得出这样几个结论: