图解CSS:Flexbox布局(Part2)
运用于Flex项目属性
运用于Flex项目中的CSS属性和运用于Flex容器上的属性类似,主要分为三个部分:对齐、尺寸 和 方向 三个部分:
align-self
在Flex容器中可以使用align-items
来控制来控制Flex项目(所有Flex容器的子元素)在Flex容器侧轴方向的对齐方式。在Flexbox布局模块中,还可以使用align-self
对单个Flex项目进行对齐方式的控制,同样是Flex容器侧轴方向的对齐。
align-self
属性可接受的值主要有:
align-self: auto | flex-start | flex-end | center | baseline | stretch;
即使在Flex容器上显式设置了align-items
用来控制所有Flex项目在Flex容器侧轴上的对齐方式,但只要在Flex项目上显式设置了align-self
的值,则会覆盖align-items
属性。只不过只对显式设置了align-self
的Flex项目生效。比如下面这个示例,我们在Flex容器显式设置了align-items
的值为center
,并且对单数Flex项目显式设置align-self
的值:
.flex__container {
align-items: center;
direction: ltr;
writing-mode: horizontal-tb;
flex-flow: row nowrap;
}
.flex__item:nth-child(odd) {
algin-self: var(--flexSelf);
}
你将看到的效果如下:
这里需要特别提出的一点是:Flex项目的align-self
显式设置值为auto
时不会覆盖Flex容器的align-items
;另外如果在Flex项目上显式设置margin
的值为auto
时,Flex项目的align-self
值将会失效。
同样的,algin-self
也会受direction
和writing-mode
取值的影响。
当Flex容器中有多行时,每一行会进行独立的布局,其中align-self
属性每次仅作用于单独一行上的Flex项目;同时每一行侧轴长度指定为可容纳该行中的Flex项目(在依据align-self
属性进行对齐之后)的最小值,并且这些行依据align-content
属性在Flex容器中进行对齐。
order
在Flexbox布局模块中,我们除了在Flex容器上通过flex-direction
来改变DOM源的顺序之外,还可以在Flex项目上显式设置order
属性来改变DOM源顺序:
order: 0 || <number>
其中0
为order
的默认值,同时它可以接受正负值(整数值)。
比如上面示例中,每组中的第二个Flex项目order
设置了不同的值:
- 第一组中Flex项目未显式设置
order
值(即默认值为0
) - 第二组中第二个Flex项目显式设置
order
的值为1
,这个时候该Flex项目会排列在最末尾 - 第三组中第二个Flex项目显式设置
order
的值为-1
,这个时候该Flex项目会排列在最前面
其中order
值越大,Flex项目越后;反之越前:
order
用来控制Flex项目顺序在布局中非常的实用,比如我们要构建一个多列布局:
<!-- HTML -->
<header>Header Section</header>
<main>
<article>Article Section</article>
<nav>Nav Section</nav>
<aside>Aside Section</aside>
</main>
<footer>Footer Section</footer>
其中<article>
放在<nav>
和<aside>
前面,主要是为了内容为先。针对这样的DOM结构,如果我们希望<nav>
在<article>
左侧,<aside>
在<article>
右侧时,order
属性就可以起关键性的作用:
上面的示例中,在main:hover
改变了<nav>
和<aside>
两个元素的order
值:
nav {
order: -1;
}
main:hover nav {
order: 1;
}
main:hover aside {
order: -1;
}
这个时候看到的效果如下:
flex
flex
属性主要用来让Flex项目如何伸长或缩短以适应Flex容器中的可用空间。它是一个简写属性,即:
flex: [<flex-grow><flex-shrink>?||<flex-basis>]
其中:
flex-grow: <number>
flex-shrink: <number>
flex-basis: content | <width>
这几个属性都有其初始值:
flex-grow
的初始值为0
flex-shrink
的初始值为1
flex-basis
的初始值为auto
即 flex
的初始值为0 1 auto
。换句话说,如果需要重置flex
属性的话,可以将其值设置为none
。它的实际语法是:
flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
在Flexbox布局模块中,flex
是一个复杂的属性,从上面的语法规则上不难发现,flex
属性可以指定 1
个值(单值语法)、2
个值(双值语法) 或 3
个值(三值语法)。
单值语法:值必须为以下其中之一:
- 一个无单位的数(
<number>
),比如flex: 1
,这个时候它会被当作<flex-grow>
的值 - 一个有效的宽度(
width
)值,比如flex: 30vw
,这个时候它会被当作<flex-basis>
的值 - 关键词
none
、auto
或initial
(即初始值)
有关于
initial
更详细的介绍,可以阅读《图解CSS:CSS层叠和继承》一文。
双值语法:第一个值必须为一个无单位数值,并且它会被当作<flex-grow>
的值;第二个值必须为以下之一:
- 一个无单位的数(
<number>
),它会被当作<flex-shrink>
的值 - 一个有效的宽度(
width
)值,它会被当作<flex-basis>
的值
比如:
flex: 1 2 » <flex-grow> <flex-shrink> » flex-grow: 1; flex-shrink: 2
flex: 2 30vw » <flex-grow> <flex-basis> » flex-grow: 2; flex-basis: 30vw
三值语法:
- 第一个值必须是一个无单位数(
<number>
),并且它会被当作<flex-grow>
的值 - 第二个值必须是一个无单位数(
<number>
),并且它会被当作<flex-shrink>
的值 - 第三个值必须为一个有效的宽度(
width
)值,并且它会被当作<flex-basis>
的值
比如:
flex: 1 2 30vw » <flex-grow> <flex-shrink> <flex-basis>
//等效于
flex-grow: 1;
flex-shrink: 2;
flex-basis: 30vw;
综合上述,flex
属性的取值可以是:
auto
:Flex项目会根据自身的width
和height
来确定尺寸,但Flex项目根据Flex容器剩余空间进行伸缩。其相当于flex: 1 1 auto
initial
:Flex项目会根据自身的width
和height
来设置尺寸。它会缩短自身以适应Flex容器,但不会伸长并吸收Flex容器中的额外剩余空间来适应Flex容器。其相当于flex: 0 1 auto
none
:Flex项目会根据自身的width
和height
来设置尺寸。它是完全非弹性的(既不会缩短,也不会伸长来适应Flex容器)。其相当于flex: 0 0 auto
<flex-grow>
:定义Flex项目的flex-grow
属性,取值为<number>
<flex-shrink>
:定义Flex项目的flex-shrink
属性,取值为<number>
<flex-basis>
:定义Flex项目的flex-basis
属性。若值为0
,则必须加上单位,以免被视作伸缩性
在大多数情况下,开发者需要将flex
设置为auto
、initial
、none
或一个无单位的正整数。比如下面这个示例,你可以尝试着调整Flex容器的大小,来观察flex
取这些值的差异:
在上面示例的基础上,我们可以给每个Flex项目的flex
设置三个值:
事实上,flex
在Flexbox布局模块中是一个复杂的属性,为了更好的向大家阐述清楚该属性,接下来主要分三个部分来阐述。即flex
的三个子属性:flex-grow
(扩展比率)、flex-shrink
(收缩比率)和 flex-basis
(伸缩基准)。这三个属性可以控制Flex项目,具体的表现如下:
flex-grow
:设置Flex项目的扩展比率,让Flex项目得到(扩展)多少Flex容器剩余空间(Positive Free Space),即Flex项目可能会变大flex-shrink
:设置Flex项目收缩比率,让Flex项目减去Flex容器不足的空间(Negative Free Space),即Flex项目可能会变小flex-basis
:Flex项目未扩展或收缩之前,它的大小,即指定了Flex项目在主轴方向的初始大小
也就是说,flex-grow
、flex-shrink
和flex-basis
可以用来指定或改变Flex项目的大小。在具体介绍这几个属性之前,先给大家介绍几个概念。
剩余空间和不足空间
在Flexbox布局模块中,Flex容器中可能会包含一个或多个Flex项目。而Flex容器和Flex项目都有其自身的尺寸大小,那么就会有Flex项目尺寸大小之和大于或小于Flex容器的情景:
- 当所有Flex项目尺寸大小之和小于Flex容器时,Flex容器就会有多余的空间没有被填充,那么这个空间就被称为 Flex容器的剩余空间(Positive Free Space)
- 当所有Flex项目尺寸大小之和大于Flex容器时,Flex容器就没有足够的空间容纳所有Flex项目(Flex项目会溢出Flex容器),那么多出来的这个空间就被称为不足空间(Negative Free Space),也被称为负空间
举个例子向大家阐述这两种情形:“假设我们有一个Flex容器,显式给其设置了width
为80vw
,并且该Flex容器中包含了四个Flex项目,每个Flex项目的width
为10vw
”。这个时候所有Flex项目的宽度总和则是10vw x 4 = 40vw
,那么Flex容器将会有剩余空间,即80vw - 40vw = 40vw
。这个 40vw
就是Flex容器的剩余空间:
再来看第二种情形:“假设Flex容器width
为40vw
,并且每个Flex项目的width
为20vw
”。这个时候所有Flex项目的宽度总和则是20vw x 4 = 80vw
,那么Flex容器则没有足够的空间容纳Flex项目(Flex项目会溢出Flex容器),即40vw - 80vw = -40vw
。这个 **40vw
**就是我们所说的Flex容器的不足空间:
上面演示的是主轴在x
轴方向,如果主轴变成y
轴的方向,同样存在上述两种情形,只不过width
变成了height
。
CSS尺寸属性和值
如果你有阅读过《图解CSS: 元素尺寸的设置》、《图解CSS:CSS逻辑属性》以及《CSS 盒模型》的话,你会发现在CSS中可以通过不同的属性以及不同的值来设置元素大小。比如,在CSS中我们可以通过下面这些属性给Flex项目显式指定尺寸:
能用于上图中属性的值,可以是下面这些:
auto
:设置值为auto
时,容器的大小将会以容器的内容来计算,它将是一个自动大小;不过给min-width
、min-height
、min-inline-size
或min-block-size
设置值为auto
时,将会指定一个自动计算好的最小值none
:如果取值为none
时,元素盒子的大小是没有任何限制<length-percentage>
:使用<length>
或<percent>
指定元素的大小,在适当情况下,根据其父元素的宽度来解析百分比min-content
:如果指定了内联轴,那么min-content
对应的大小则是内联大小,否则将表现为属性的初始值,即固有的最小宽度max-content
:如果指定了内联轴,那么max-content
对应的大小则是内联大小,否则将表现为属性的初始值,即固有的首选宽度fit-content()
:如果显式指定了内联轴,使用fit-content()
函数,可以用指定的参数替换可用空间,即min(max-content, max(min-content, <length-percentage>))
;否则将表现为属性的初始值。对于内在尺寸,fit-content(<length>)
表现长度值(<length>
)。如果fit-content()
使用了百分比值,将会作为min-content
当作最小内容,max-content
作为最大内容
而其中auto
、min-content
、max-content
和fit-content()
又被称为自动计算尺寸大小方式。
在CSS中除了width
、height
、inline-size
和block-size
会影响元素尺寸大小之外,还有运用于元素盒子的属性,比如内距,边框等都会影响元素尺寸大小。有关于这方面更详细的介绍可以阅读《CSS 盒模型》一文。
内部尺寸和外部尺寸
熟悉CSS盒模型的同学都知道,根据盒子不同的边缘可以将盒子划分成不同类型的盒子,比如content-box
、padding-box
、border-box
和margin-box
:
其中content-box
对应的尺寸被称为内部尺寸,而margin-box
对应的尺寸被称为外部尺寸。
明确的尺寸和不确定的尺寸
明确的尺寸指的是不需要执行布局就可以确定盒子的大小。也就是说,显式给容器设置一个固定值、或内容所占区域的大小、或一个容器块的初始大小,或通过其他计算方式得到的尺寸(比如Flexbox布局中的“拉伸和压缩(Strretch-fit)”)。
不确定的尺寸指的是一个未知的尺寸。换句话容器的大小具备无限空间(即有可能很大,也有可能很小)。
通俗一点来理解的话,明确的尺寸是知道容器的width
(或inline-size
)和height
(或block-size
);不确定的尺寸是需要根据内容来计算的,所以要知道不确定的尺寸需要先检查内容。
有了上述概念之后,我们就可以开始进入flex-grow
、flex-shrink
和flex-basis
三个属性的世界。
flex-grow
flex-grow
的语法规则很简单:
flex-grow: <number>
其中<number>
是一个正数的数值,并且其默认值是0
。
在Flexbox布局模块中,flex-grow
是一个扩展因子,也被称为扩展比例。其意思是,当Flex容器有一定的剩余空间时,flex-grow
可以让Flex项目分配Flex容器剩余的空间,每个Flex项目将根据flex-grow
因子扩展,从而让Flex项目占满整个Flex容器(有效的分配和利用Flex容器剩余空间)。
当flex-grow
的值是一个大于0
的值时,Flex项目就会占用Flex容器剩余空间。在Flexbox布局模块中,在使用flex-grow
时可以按下面的方式使用:
- 所有Flex项目设置相同的
flex-grow
值 - 每个Flex项目设置不同的
flex-grow
值
正如上例所示,在Flex项目中设置flex-grow
之后,Flex项目会根据Flex容器宽度来动态调整自身的尺寸大小:
当Flex容器没有任何剩余空间时,即使Flex项目显式设置了flex-grow
的值,Flex项目也不会扩展(因为没有空间可扩展):
正如上图所示,flex-grow
的不同设置得到的效果将会不一样,但flex-grow
的值始终总量为1
,也就是说,所有Flex项目的占有量总和(分子)和分母相同。
如果你希望能更彻底的了解flex-grow
,那就需要了解flex-grow
的计算方法。flex-grow
有相应的计算公式:
下面用一个简单的示例来阐述flex-grow
是如何分配Flex容器的剩余空间:
<!-- HTML -->
<div class="flex__container">
<div class="flex__item" style="--flexGrow: 0">flex-grow:0</div>
<div class="flex__item" style="--flexGrow: 1">flex-grow:1</div>
<div class="flex__item" style="--flexGrow: 2">flex-grow:2</div>
<div class="flex__item" style="--flexGrow: 3">flex-grow:3</div>
</div>
/* CSS */
:root {
--flexGrow: 0;
}
.flex__container {
inline-size: 80vw;
}
.flex__item {
flex-grow: var(--flexGrow);
inline-size: 10vw;
}
效果如下:
从该示例的代码中我们可以获知:
- Flex容器的宽度是
80vw
(CSS中设置了inline-size: 80vw
) - Flex容器中共有四个Flex项目,并且每个Flex项目的宽度是
10vw
(CSS中设置了inline-size: 10vw
) - Flex项目宽度总和为
10vw x 4 = 40vw
- Flex容器的剩余空间为
80vw - 40vw = 40vw
- Flex项目的
flex-grow
的值分别是0
、1
、2
和3
,所有Flex项目的flex-grow
总和为0 + 1 + 2 + 3 = 6
即:
flex-grow 公式中变量名称 |
Flex项目1 | Flex项目2 | Flex项目3 | Flex项目4 | 总数 |
---|---|---|---|---|---|
Flex项目的flex-grow 值 |
0 |
1 |
2 |
3 |
0 + 1 + 2 + 3 = 6 |
Flex项目的宽度(width ) |
10vw |
10vw |
10vw |
10vw |
10vw x 4 = 40vw |
Flex容器的宽度(width ) |
80vw |
||||
Flex容器的剩余空间 | 80vw - 40vw = 40vw |
||||
Flex项目新宽度(width ) |
? |
? |
? |
? |
有了这些参数值之后,根据上面提供的flex-grow
计算公式就可以计算出每个Flex项目的宽度:
» 计算之后的Flex项目宽度 = (Flex项目的flex-grow值 ÷ 所有Flex项目的flex-grow总值 × Flex容器剩余空间 ) + Flex项目初始宽度
» 计算之后的Flex1项目宽度 = (0 ÷ 6 × 40 ) + 10 = 10vw
» 计算之后的Flex2项目宽度 = (1 ÷ 6 × 40 ) + 10 = 16.667vw
» 计算之后的Flex3项目宽度 = (2 ÷ 6 × 40 ) + 10 = 23.333vw
» 计算之后的Flex4项目宽度 = (3 ÷ 6 × 40 ) + 10 = 30vw
具体如下图所示:
上面的示例中flex-grow
的取值都是正整数值。事实上,flex-grow
还可以设置为小数值。在上例的基础上,将Flex项目的flex-grow
值分别设置为0
、 0.1
、0.2
和0.3
,此时所有Flex项目的flex-grow
总和为0.6
。虽然取值为小数,但其计算方式是一样的,只不过所有flex-grow
值的总和小于1
时,Flex容器的剩余空间不会完全分配完(最终还会有剩余的空间)。换句话说,就该例而言,Flex项目只分完了Flex容器剩余空间的60%
。最终的效果如下:
虽然在Flex项目中显式设置了flex-grow
的值(大于或等于1
)时,Flex项目会根据相应的比率得到Flex容器剩余空间,Flex项目宽度会得到扩展(变宽),但这并不是绝对的,如果Flex项目上显式设置了max-width
或max-inline-size
时,Flex项目会受到它们的限制。比如下面这个示例,我们显式在Flex项目中设置max-inline-size: 18vw
,得到的效果如下:
在上面的示例中,为了更好的向大家演示flex-grow
的计算,在代码中给Flex项目显式的设置了inline-size
的值为10vw
(相当于设置了flex-basis
值)。后面我们会详细的介绍flex-basis
的使用。从示例的效果中不难发现,flex-grow
和inline-size
(或flex-basis
)是会相互影响的。这也令Flex项目计算变得复杂化了,尤其是加入min-width
、min-inline-size
、max-widt
和max-inline-size
会更为复杂。
为什么这么说呢?我们用一个示例来举例。比如说,不在Flex项目上显式的设置inline-size
、width
或flex-basis
的值。根据我们所掌握的CSS知识可以得知,这个时候,Flex项目的大小会取决于其内容的大小(相当于inline-size
或width
设置了max-content
)。
示例中代码很简单:
:root {
--flexGrow: 1;
}
.flex__container {
inline-size: 80vw;
}
.flex__item {
flex-grow: var(--flexGrow);
}
示例中的四个Flex项目的flex-flow
的值都是相同的,即1
。Flex容器的宽度设置的是80vw
,具体渲染出来的容器宽度和浏览器视窗有关联,同时Flex项目并未显式指定具体宽度的属性,那么其宽度会根据自身内容来决定。为了更好的向大家演示,通过浏览器调试器,将对应元素的盒模型尺寸截图示意:
从上图可以得到:
- Flex容器的宽度是
804px
- Flex项目的宽度分别是
43.36px
、92.09px
、140.83px
和189.56px
,所有Flex项目宽度的总和为465.84px
- Flex容器的剩余宽度为
804px - 465.84px = 338.16px
- 所有Flex项目的
flex-grow
值为1
将相应的值套用到flex-grow
的公式中,可以得到:
» 计算之后的Flex项目宽度 = (Flex项目的flex-grow值 ÷ 所有Flex项目的flex-grow总值 × Flex容器剩余空间 ) + Flex项目初始宽度
» 计算之后的Flex1项目宽度 = (1 ÷ 4 × 338.16) + 43.36 = 127.9px
» 计算之后的Flex2项目宽度 = (1 ÷ 4 × 338.16) + 92.09 = 176.63px
» 计算之后的Flex3项目宽度 = (1 ÷ 4 × 338.16) + 140.83 = 225.37px
» 计算之后的Flex4项目宽度 = (1 ÷ 4 × 338.16) + 189.56 = 274.1px
注意,不同的浏览器对小数处理有差异。
flex-shrink
flex-shrink
和flex-grow
有点类似,不同的是 flex-shrink
是用来控制Flex项目的收缩因子。通过前面的内容我们知道,在Flexbox布局中,有的时候Flex项目宽度总和会超过Flex容器宽度,造成Flex项目溢出Flex容器。对于开发者而言,总是希望Flex项目能根据容器的空间来自动调整自身的大小。针对这种场景,flex-shrink
就能起作用了。
简单地说,flex-shrink
会将Flex容器不足的空间按收缩因子的比例分配到Flex项目上,这个时候Flex项目就会根据比例收缩宽度,以免Flex项目溢出Flex容器。
flex-shrink
语法规则也很简单:
flex-shrink: <number>
flex-shrink
只接收正数值(可以是正整数,也可以是正小数),其默认值是1
。即 Flex容器没有足够空间容纳所有Flex项目时,Flex项目默认都会收缩。如果你不想让Flex项目进行收缩,可以显式设置flex-shrink
的值为 0
,此时Flex项目始终会保持原始的fit-content
宽度。
在Flexbox布局模块中,在使用flex-shrink
时也可以按下面的方式使用:
- 所有Flex项目设置相同的
flex-shrink
值 - 每个Flex项目设置不同的
flex-shrink
值
正如上例所示,在Flex项目中不做任何设置,Flex项目会按相同的比例收缩(因为flex-shrink
的默认值是1
);显式设置flex-shrink
为0
时,Flex项目不会收缩,将会溢出Flex容器;不同的Flex项目设置不同flex-shrink
值时会按不同的比例收缩:
同样的,flex-shrink
也有一套相应的计算公式:
flex-shrink
计算公式看上去要比flex-grow
复杂一些,过程也多一点,但只要你真正理解了,就不会再有这样的感觉了。同样用实例来向大家阐述,这样更易于理解flex-shrink
是如何计算,以达到Flex项目根据收缩因子自动收缩(或者说分配Flex容器不足空间):
<!-- HTML -->
<div class="flex__container">
<div class="flex__item" style="--flexShrink: 0">flex-shrink:0</div>
<div class="flex__item" style="--flexShrink: 1">flex-shrink:1</div>
<div class="flex__item" style="--flexShrink: 2">flex-shrink:2</div>
<div class="flex__item" style="--flexShrink: 3">flex-shrink:3</div>
</div>
/* CSS */
:root {
--flexShrink: 0;
}
.flex__container {
inline-size: 40vw;
}
.flex__item {
flex-shrink: var(--flexShrink);
inline-size: 15vw;
}
效果如下:
从该示例的代码中我们可以获知:
- Flex容器的宽度是
40vw
(CSS中设置了inline-size: 40vw
) - Flex容器中共有四个Flex项目,并且每个Flex项目的宽度都是
15vw
(CSS中设置了inline-size: 15vw
) - Flex项目宽度总和为
15vw x 4 = 60vw
- Flex容器的不足空间为
40vw - 60vw = -20vw
- Flex项目的
flex-shrink
的值分别是0
、1
、2
和3
,所有Flex项目的flex-shrink
总和为0 + 1 + 2 + 3 = 6
即:
flex-shrink 公式中变量名称 |
Flex项目1 | Flex项目2 | Flex项目3 | Flex项目4 | 总数 |
---|---|---|---|---|---|
Flex项目的flex-shrink 值 |
0 |
1 |
2 |
3 |
0 + 1 + 2 + 3 = 6 |
Flex项目的宽度(width ) |
15vw |
15vw |
15vw |
15vw |
15vw x 4 = 60vw |
Flex容器的宽度(width ) |
40vw |
||||
Flex容器的剩余空间 | 40vw - 60vw = -20vw |
||||
Flex项目新宽度(width ) |
? |
? |
? |
? |
有了这些参数值之后,根据上面提供的flex-shrink
计算公式就可以计算 总收缩宽度 和 Flex项目收缩比例。 我们先来计算出总收缩宽度:
» 总收缩宽度 = ∑(Flex项目初始宽度 × Flex项目的flex-shrink值)
» 总收缩宽度 = (15vw × 0) + (15vw × 1) + (15vw × 2) + (15vw × 3) = 15vw × 6 = 90vw
有了总收缩宽度就可以计算出 Flex项目的收缩比例:
» Flex项目的收缩比例 = (Flex项目的初始宽度 × Flex项目的flex-shrink值) ÷ Flex项目总收缩宽度
» Flex项目1的收缩比例 = (15vw × 0) ÷ 90vw = 0
» Flex项目2的收缩比例 = (15vw × 1) ÷ 90vw = 0.1667
» Flex项目3的收缩比例 = (15vw × 2) ÷ 90vw = 0.3333
» Flex项目4的收缩比例 = (15vw × 3) ÷ 90vw = 0.5
这样就可以计算出Flex项目的新宽度:
» Flex项目新宽度 = Flex项目初始宽度 - (Flex容器不足空间 × Flex项目收缩比例)
» Flex项目1新宽度 = 15vw - (20vw × 0) = 15vw
» Flex项目2新宽度 = 15vw - (20vw × 0.1667) = 11.666vw
» Flex项目3新宽度 = 15vw - (20vw × 0.3333) = 8.334vw
» Flex项目4新宽度 = 15vw - (20vw × 0.5) = 5vw
具体如下图所示:
其实flex-shrink
还有另一套相对来说更为简单的计算公式:
根据上图的公式,计算出来的结果和前面计算的结果基本相等:
» Flex项目的新宽度 = Flex项目初始宽度 - (Flex容器不足空间 ÷ 所有Flex项目flex-shrink和) × Flex项目的flex-shrink
» Flex项目1的新宽度 = 15vw - (20vw ÷ 6) × 0 = 15vw
» Flex项目2的新宽度 = 15vw - (20vw ÷ 6) × 1 = 11.667vw
» Flex项目3的新宽度 = 15vw - (20vw ÷ 6) × 2 = 8.333vw
» Flex项目4的新宽度 = 15vw - (20vw ÷ 6) × 3 = 5vw
同样的,flex-shrink
也可以取小数值,而且和flex-grow
相似,如果所有Flex项目的flex-shrink
总和小于1
时,Flex容器的不足空间不会全部分完,Flex项目依旧会溢出Flex容器,比如下面这个示例:
另外,在Flexbox布局当中,会阻止Flex项目宽度缩小至0
。如果某个Flex项目按照flex-shrink
计算出来的新宽度趋向于0
时,Flex项目将会按照该元素的min-content
的大小来设置宽度,同时这个宽度将会转嫁到其他Flex项目,再按相应的收缩因子进行收缩。
在这个示例中,第四个Flex项目的flex-shrink
的值为9
。根据上面提供的公式,可以获知:
» Flex项目的新宽度 = Flex项目初始宽度 - (Flex容器不足空间 ÷ 所有Flex项目flex-shrink和) × Flex项目的flex-shrink
» Flex项目4的新宽度 = 15vw - (20vw ÷ 12) × 9 = 0
计算出来的宽度为0
,但实际上这个时候渲染出来的宽度是该项目的min-content
(该示例就是“shrink”单词的宽度),大约47.95px
(约3.66vw
)。那么这个值将会分成3
份(因为该例另外三个Flex项目的flex-shrink
是0
、1
和2
),并且对应的Flex项目会继续分配本应Flex项目4要收缩的宽度。即:
» Flex项目1新宽度 = 15vw - 20 ÷ 12 × 0 - 3.66 ÷ 3 × 0 = 15vw (约196.5px)
» Flex项目2新宽度 = 15vw - 20 ÷ 12 × 1 - 3.66 ÷ 3 × 1 = 12.113vw (约158.6847px)
» Flex项目3新宽度 = 15vw - 20 ÷ 12 × 2 - 3.66 ÷ 3 × 2 = 9.227vw (约120.869px)
浏览器视窗宽度在1310px
状态下渲染出来的结果如下:
在Flexbox项目中,虽然flex-shrink
会让Flex项目根据收缩因子缩小自身宽度,但是Flex项目自身显式设置了min-width
或min-inline-size
时,计算出来的值要是小于min-width
(或min-inline-size
),Flex项目将会按min-width
或min-inline-size
的值渲染:
这个时候,设置了min-width
(或min-inline-size
)的Flex项目会类似于上例中的min-content
一样,将未分配的Flex容器不足空间转嫁到其他的Flex项目上。
相对而言,
flex-shrink
的计算要比flex-grow
复杂一些,特别是当Flex项目根据flex-shrink
计算出来的宽度趋于0
时,会让计算变得复杂化。
在Flexbox布局模块中,基于前面提到的Flex容器的对齐属性、Flex项目中的flex-shrink
和flex-grow
我们就可以很好的处理Flex容器的剩余空间和不足空间:
- Flex容器有剩余空间(所有Flex项目的宽度总和小于Flex容器的宽度),如果设置了
flex-grow
,Flex项目会根据扩展因子分配Flex容器剩余空间;在未设置flex-grow
时,在Flex容器中是否设置了对齐方式,如果是,那么会按对齐方式分配Flex容器剩余空间,如果不是,Flex容器剩余空间不变 - Flex容器有不足空间(所有Flex项目的宽度总和大于Flex容器的宽度),如果设置了
flex-shrink
值为0
,Flex项目不会收缩,Flex项目溢出Flex容器;如果未显式设置flex-shrink
值,Flex项目分平均分配Flex容器不足空间,Flex项目会变窄(Flex项目的flex-shrink
的默认值为1
),如果显式设置了flex-shrink
的值为非0
的不同值,那么Flex项目会按照不同的收缩因子分配Flex容器不足空间,Flex项目同样会变窄
具体的我们可以绘制一张这方面的流程图:
flex-basis
在Flexbox布局模块中,除了我们熟悉的width
、min-width
、max-width
以及逻辑属性inline-size
、min-inline-size
和max-inline-size
可以用来设置Flex项目的初始宽度之外,还可以使用flex-basis
属性,即 在任何Flex容器空间(剩余空间或不足空间)分配发生之前初始化Flex项目尺寸。其语法很简单:
flex-basis: content | <width>
其默认值为auto
。其中content
是指Flex项目内容的自动尺寸,而<width>
是我们熟悉的带有数值和单位的值,比如10vw
、50%
、200px
等;除此之外,还可以是一些用来描述宽度的关键词,比如fill
、max-content
、min-content
和fit-content()
、inherit
、initial
和unset
等。
如果flex-basis
的值设置为auto
,浏览器将先检查Flex项目的主尺寸是否设置了绝对值,再计算Flex项目的初始值。比如说,你给Flex项目显式设置了width: 200px
,那么200px
就是Flex项目的flex-basis
值。
如果你的Flex项目可以自动调整大小,则auto
会解析为其内容的大小,这个时候min-content
和max-content
会起作用,并且Flex项目的max-content
作为flex-basis
的值。
相比flex-grow
和flex-shrink
而言,flex-basis
是最复杂的部分。事实上,在Flexbox布局模块中 设置Flex项目的尺寸大小存在一个隐式的公式:
content
➜width
➜flex-basis
简单地说,如果Flex项目未显式指定flex-basis
的值,那么flex-basis
将回退到width
(或inline-size
)属性;如果未显式指定width
(或inline-size
)属性的值,那么flex-basis
将回退到基于Flex项目内容计算宽度。不过,决定Flex项目尺寸大小,还受flex-grow
和flex-shrink
以及Flex容器大小的影响。而且Flex项目 最终尺寸 会受min-width
、max-width
(或min-inline-size
、max-inline-size
)属性限制。这一点必须得注意。
接下来用一个简单的示例来阐述content
、width
以及flex-basis
对Flex项目尺寸的影响。
其中
width
是用来描述元素宽度的物理属性,从《图解CSS:CSS逻辑属性》一文中可以得知,与其相匹配的还有inline-size
或block-size
逻辑属性。注意,CSS逻辑属性受direction
和writing-mode
的值的影响。
示例代码很简单:
<!-- HTML -->
<div class="flex__container">
<div class="flex__item"></div>
<div class="flex__item"></div>
<div class="flex__item"></div>
<div class="flex__item"></div>
</div>
/* CSS */
.flex__container {
width: 600px;
display: flex;
border: 1px dashed #f36;
align-items: stretch;
}
按照上面的公式节奏来走,慢慢的加戏。
Flex项目不显式的设置任何与尺寸大小有关系属性,即用
content
来撑开Flex项目。
在上面的HTML基础上添加内容:
<!-- HTML -->
<div class="flex__container">
<div class="flex__item">Lorem ipsum dolor sit amet</div>
<div class="flex__item">Lorem ipsum dolor sit amet consectetur adipisicing elit</div>
<div class="flex__item">Fugiat dolor nihil saepe. Nobis nihil minus similique hic quas mollitia.</div>
<div class="flex__item">Lorem ipsum dolor sit amet consectetur adipisicing elit. Molestias consequuntur sequi suscipit iure fuga ea!</div>
</div>
这个时候你看到的效果如下,而且每个Flex项目的宽度(以width
为例)都依赖于元素内容的多少来控制:
注意,文本相关的CSS属性,比如font-size
和font-family
等属性此时会影响Flex项目尺寸的大小。
在这个示例中,并没有显式给Flex项目设置flex-basis
属性,此时flex-basis
会取默认值auto
:
正如上图所示,浏览器将flex-basis
属性的值以auto
计算。此时 flex-basis
的值会采用Flex项目内容计算出宽度:
- 第一个Flex项目计算出来的宽度值为
62.61px
- 第二个Flex项目计算出来的宽度值为
126.55px
- 第三个Flex项目计算出来的宽度值为
161.06px
- 第四个Flex项目计算出来的宽度值为
247.78px
接下来看第二步:
显式给Flex项目设置
width
值。
给所有Flex项目设置width: 120px
:
:root {
--width: 120px;
}
.flex__item {
width: var(--width);
}
效果如下:
这个时候所有Flex项目宽度都是相等的:
这个示例中,只是显式给所有Flex项目设置了width: 120px
。浏览器计算出来的flex-basis
值依旧为auto
,但显式的设置了width: 120px
,最终width
属性的值决定了Flex项目的尺寸大小。
继续加码:
显式给Flex项目设置
flex-basis
值,即Flex项目同时有width
和flex-basis
值。
为了更好的区分flex-basis
和width
,在示例中把flex-basis
和width
设置不同的值:
:root {
--width: 120px;
--flexBasis: 150px;
}
.flex__container {
width: 800px;
}
.flex__item {
width: var(--width);
flex-basis: var(--flexBasis);
}
效果如下:
在这个示例中,虽然在Flex项目同时显式设置了width
和flex-basis
,但Flex项目最终的尺寸大小采用了flex-basis
的值:
也就是说,同时显式给Flex项目设置flex-basis
和width
的值,则width
会被忽略,不再起作用,相当于没有设置。
在该示例中,所有Flex项目宽度的总和是 600px
,小于Flex容器宽度 800px
,Flex容器的剩余宽度为200px
:
如果所有Flex项目宽度的总和大于Flex容器宽度会怎么样呢?我想你可能知道结果了,但这里还是花点时间再向大家印证一下。我们在上例的基础上将Flex项目的flex-basis
值调整为300px
:
:root {
--width: 120px;
--flexBasis: 300px;
}
效果如下:
当你没看到上面的渲染效果时,你是否会认为,Flex项目应该是取值flex-basis
,即300px
。Flex项目会溢出Flex容器:
如果你真的是这么的认为,那说明你没有仔细阅读前面有关于flex-shrink
的内容。事实上,Flex项目的宽度并不是width
的值150px
,也不是flex-basis
的值300px
,而是一个被重新计算后的值 199.5px
:
这就是Flexbox布局模块的强大之处,会有这样的结果都归功于flex-shrink
属性。从前面的内容我们可以得知,当Flex容器有不足空间(该示例为200px
),并且未显式设置flex-shrink
属性,那么浏览器将会取其默认值1
作为收缩因子,并且根据flex-shrink
计算公式计算出每一个Flex项目应该在原有宽度300px
上减去多少应该收缩的宽度:
» Flex项目的新宽度 = Flex项目初始宽度 - (Flex容器不足空间 ÷ 所有Flex项目flex-shrink和) × Flex项目的flex-shrink
» Flex项目的新宽度 = 300px - (402px ÷ 4) × 1 = 199.5px
注意,在该示例中,Flex容器设置了
1px
的边框,计算出来Flex容器不足的空间为402px
。
前面提到过,在Flexbox布局模块中影响Flex项目尺寸大小应该根据其隐式公式(即 content
➜ width
➜ flex-basis
)来进行判断。如果要显式给Flex项目设置尺寸大小,其最佳方式是 使用flex-basis
,而不是width
(或inline-size
)。
最后还有一点千万别忘记:
使用
flex-basis
时会受min-width
和max-width
(或逻辑属性中min-inline-size
或max-inline-size
)的限制。
在详细阐述这一点之前,我们先来回忆一下。
在CSS中除了通过width
(或height
)显式给一个盒子定义尺寸之外,还可以通过min-width
(或min-height
)和max-width
(或max-height
)来定义盒子尺寸。如果拿width
、min-width
和max-width
同时给一个盒子设置宽度时,具体取值为哪个?这是一个很有意思的问题。我们可以通过这三者之间的权重来取决:
- 元素的
width
大于max-width
时,元素的width
等于max-width
,即max-width
能覆盖width
- 元素的
width
小于min-width
时,元素的width
等于min-width
,即min-width
能覆盖width
- 当
min-width
大于max-width
时,min-width
优先级将高于max-width
这个规则同样适用于inline-size
、min-inline-size
和max-inline-size
。
同样的,当flex-basis
、min-width
和max-width
同时出现时,也可以套用上面的规则,最终客户端会决定哪个值运用于Flex项目上。这就是为什么说 flex-basis
会受制于min-width
和max-width
。 事实上,我们在介绍flex-grow
和flex-shrink
时也提到过,当flex-grow
计算出来的值大于max-width
值时,会取max-width
的值;当flex-shrink
计算出来的值小于min-width
值时,会取min-width
值。
如果Flex项目同时出现width
、flex-basis
和min-width
时,具体的运算过程如下:
- 根据法则:
content
➜width
➜flex-basis
,判断出运用于Flex项目的值,即flex-basis
会运用于Flex项目 - 再根据法则:Flex项目的
width
小于min-width
时,Flex项目的width
等于min-width
,即min-width
能覆盖width
这样一来,如果flex-basis
小于min-width
时,Flex项目的宽度会取值min-width
,即min-width
覆盖flex-basis
。
如果Flex项目同时出现width
、flex-basis
和max-width
时,具体的运算过程如下:
- 根据法则:
content
➜width
➜flex-basis
,判断出运用于Flex项目的值,即flex-basis
会运用于Flex项目 - 再根据法则:Flex项目的
width
大于max-width
时,Flex项目的width
等于max-width
,即max-width
能覆盖width
这样一来,如果flex-basis
大于max-width
时,Flex项目的宽度会取值max-width
,即max-width
覆盖flex-basis
。
如果Flex项目同时出现width
、flex-basis
、min-width
和max-width
时,会在上面的规则上增加新的一条规则来进行判断:
当
min-width
大于max-width
时,min-width
优先级将高于max-width
。
那么套用到Flex项目中:
flex-basis
大于max-width
,Flex项目的宽度等于max-width
,即max-width
能覆盖flex-basis
flex-basis
小于min-width
时,Flex项目的宽度会取值min-width
,即min-width
覆盖flex-basis
由于min-width
大于max-width
时会取min-width
,有了这个先取条件我们就可以将flex-basis
和min-width
做权重比较,即:flex-basis
会取min-width
。反过来,如果min-width
小于max-width
时则依旧会取max-width
,同时要是flex-basis
大于max-width
就会取max-width
。
如果你理解了的话,可以使用更简单的规则来决定用于Flex项目的尺寸。
首先根据
content
➜width
➜flex-basis
来决定用哪个来决定用于Flex项目。如果Flex项目显式设置了flex-basis
属性,则会忽略content
和width
。而且min-width
是用来设置Flex项目的下限值;max-width
是用来设置Flex项目的上限值。
虽然使用flex-basis
显式设置Flex项目的尺寸优于width
或height
,但这仅是理想尺寸。因为在Flexbox布局中就算是你显式设置了flex-basis
也不能保证这将是Flex项目的最终尺寸。那是 Flex项目放入Flex容器之后一切都将有可能改变。前面也提到过了,Flex项目的实际尺寸大小会被flex
的另外两个属性flex-grow
和flex-shrink
的影响。简单地说,如果容器有足够多的空间,Flex项目会根据扩展因子扩展到足够填充Flex容器;反之,如果容器没有足够多的空间容纳Flex项目时,Flex项目会根据收缩因子缩小到足够被容纳在Flex容器中。
怎么设置Flex项目的基本大小
通过前面的学习,我们可以获知,对于Flex项目,显式设置flex-basis
是一个最佳方案。但对于如何设置Flex项目的基本大小,我们可以围绕以下几点来进行思考:
flex-basis
的值是auto
?Flex项目显式的设置了宽度吗?如果设置了,Flex项目的大小将会基于设置的宽度flex-basis
的值是auto
还是content
?如果是auto
,Flex项目的大小为原始大小flex-basis
的值是0
的长度单位吗?如果是这样那这就是Flex项目的大小flex-basis
的值是0
呢? 如果是这样,则Flex项目的大小不在Flex容器空间分配计算的考虑之内
如果用简单的话来概括的话:
flex-basis
可以判断出Flex容器是存在剩余空间,还是存在不足空间- 如果Flex容器存在剩余空间,
flex-grow
可以按扩展因子来扩展Flex项目大小 - 如果Flex容器存在不足空间,
flex-shrink
可以按收缩因子缩小Flex项目大小 - 如果Flex项目显式设置了
max-width
值,即使Flex项目按照flex-grow
扩展因子扩展了Flex项目尺寸,也不会大于max-width
的值 - 如果Flex项目显式设置了
min-width
值,即使Flex项目按照flex-shrink
收缩因子收缩了Flex项目尺寸,也不会小于min-width
的值