Flex布局介绍
Flexbox 布局 已是目前最为流行的 Web 布局方式之一,它给 Web 开发者在完成页面或组件的 UI 布局带来了极大的灵活性和便利性。但也是因为它有极大的灵活性,里面隐藏了一些不为人知的细节,如果不是对 Flexbox 极为熟悉或者对其规范极为了解的话,其中有很多细节将会被遗漏,而这些细节又会让你在使用的感到困惑,甚至是带来一定的麻烦。
这次在优化 imgcook 的 Flexbox 布局时,重新阅读了一次 Flexbox 的规范,发现自己曾忽略了部分重要信息。为此在这篇文章中,将 Flexbox 布局,CSS 的书写模式,逻辑属性,对齐方式结合在一起整理了一篇笔记,希望对于想了解或使用 Flexbox 碰到痛楚的同学有所帮助。
一些术语和概念
Flexbox 术语
术语的统一有助于我们后面更好的讨论和解决问题。用下图来描述 Flexbox 中的术语:
主轴和侧轴只有在 Flexbox 布局体系中才有这样的概念,并不是水平方向永远都是主轴(Main Axis),垂直方向永远是侧轴(Cross Axis)。主轴和侧轴除了会受 Flexbox 中的
flex-direction
取值的影响之外,还会受 CSS 的书写模式writing-mode
和direction
以及 HTML 的dir
属性的影响!
块轴和内联轴
CSS Box Alignment Module Level 3 引入了两个新的概念,即 块轴(Block Axis)和 内联轴(Inline Axis) :
- 块轴 是沿 块 (block) (比如段落元素)的布局方向延伸的轴, 它会垂直穿过行内轴
- 内联轴 是在使用特定写作模式中,沿句子单词的流动方向的轴。比如对于英语或者中文来说, 内联轴是水平的
同时 块轴(Block Axis)又常称为列(Column), 内联轴(Inline Axis)又常称为行(Row):
虽然目前为止在 Flexbox 规范和 Grid 规范中都有自身关于对齐方式的描述,但 CSS 中有关于对齐方式都将收口到 Box Alignment 模块中;为此后面对说轴的说法更多的是 “ 块轴 ” 和 “ 行内轴 ”,结合到 Flexbox 布局中,轴的对应关系是:
- 行内轴(Inline Axis)也对标 Flexbox 中的主轴 Main Axis
- 块轴(Block Axis)也对标 Flexbox 中的侧轴 Cross Axis
块轴和行内轴同样受 CSS 的书写模式
writing-mode
和direction
以及 HTML 的dir
属性影响。只不过,在 Flexbox 布局中,还受flex-direction
属性的影响。
书写模式
CSS Writing Modes Level 3 规范中的 writing-mode
和 direction
以及 HTML 中的 dir
属性对于 Flexbox 中的主轴和侧轴都会有影响,将会改变主轴和侧轴的方向。
逻辑属性
块轴 、 内联轴 、 书写模式 的出现之后,就有了 块起点(Block Start)、块终点(Block End)、内联起点(Inline Start)和 内联终点(Inline End):
如果放到 Flexbox 布局中:
- 内联起点(Inline Start) 等同于 Flexbox 布局中的 主轴起点(Main Start)
- 内联终点(Inline End) 等同于 Flexbox 布局中的 主轴终点(Main End)
- 块起点(Block Start) 等同于 Flexbox 布局中的 侧轴起点(Cross Start)
- 块终点(Block End) 等同于 Flexbox 布局中的 侧轴终点(Cross End)
同时 CSS Logical Properties and Values Level 1 规范中引入了 block-start
、 block-end
、 inline-start
和 inline-end
, 但它们和 Flexbox 中,Grid 中以入 Box Alignment 中的 flex-start
、 start
、 flex-end
和 end
是不等同的,也不是同一领域中的概念。这几个属性对应的是物理属性中的 top
、 right
、 bottom
和 left
:
也就是说,引入 CSS 逻辑属性之后,CSS 盒模型将分 物理盒模型 和 逻辑盒模型 :
CSS 属性也从此之后有 逻辑属性 和 物理属性 之分:
注意,CSS 逻辑属性也受 CSS 书写模式
writing-mode
、directioin
属性和 HTML 的dir
属性影响,而且不同组合之下也不同:
剩余空间(可用空间)和 不足空间
在 Flexbox 布局模块中,Flex 容器中可能会包含一个或多个 Flex 项目。而 Flex 容器和 Flex 项目都有其自身的尺寸大小,那么就会有 Flex 项目尺寸大小之和大于或小于 Flex 容器的情景:
- 当所有 Flex 项目尺寸大小之和小于 Flex 容器时,Flex 容器就会有多余的空间没有被填充,那么这个空间就被称为 Flex 容器的剩余空间(Positive Free Space)
- 当所有 Flex 项目尺寸大小之和大于 Flex 容器时,Flex 容器就没有足够的空间容纳所有 Flex 项目(Flex 项目会溢出 Flex 容器),那么多出来的这个空间就被称为不足空间(Negative Free Space),也被称为负空间
Flex 容器 和 Flex 项目
在元素上使用 display
设置值为 flex
或 inline-flex
,该容器会成为 Flex 容器,该容器下的子元素,包括 文本节点,伪元素。
使用 flex
和 inline-flex
的具体场景:
如果元素显式设置了 display
的值为 flex
或 inline-flex
,Flex 项目在未显式设置与尺寸大小有关的属性时,Flex 项目都将会按其内容大小来计算自身大小。
- 设置为
display: flex
时,Flex 容器未显式设置与宽度相关的属性时,其宽度与其父容器等同(相当于width: 100%
) - 设置为
display: inline-flex
时,Flex 容器未显式设置与宽度相关的属性时,其宽度等同于所有 Flex 项目的宽度和
当 Flex 容器中所有 Flex 项目所有宽和大于 Flex 容器时:
- 设置为
display: flex
时,Flex 项目会溢出 Flex 容器 - 设置为
display: inline-flex
时,Flex 项目会撑大 Flex 容器,有可能造成 Flex 容器溢出其父元素(或祖先元素)
使用 display: inline-flex
时最好结合 min-width
和 min-height
一起使用。不建议显式设置 width
和 height
。
display
设置为 flex
时,Flex 容器从表现形式上类似于块容器,事实它是一个 Flex 容器,上下文格式是 FFC(Flexbox Formatting Content),因此运用于块容器(Block Formatting Content)上的一些布局属性就不再适用,比如:
- CSS 的
column-*
属性在 Flex 容器上不起作用 - CSS 的
float
和clear
属性在 Flex 项目上不起作用,也不会让 Flex 项目脱离文档流 - CSS 的
vertical-align
属性在 Flex 项目上不起作用 - CSS 伪元素
::first-line
和::first-letter
在 Flex 容器上不起作用,而且 Flex 容器不会为其祖先提供首行或首字母格式化
有一点需要注意, 如果元素的 display
的值为 inline-flex
,并且该元素显式的设置了 float
或 position
的值为 relative
、 absolute
或 fixed
,那么 display
的计算值是 flex
, 即 Flex 容器表现行为和 display: flex
等同 。
运用于 Flex 容器上的属性
指定主轴方向
在 Flex 容器中显式使用 flex-direction
可以指定主轴方向,如果未显式设置 flex-direction
属性,Flex 容器则会采用其默认值 row
。
上图展示的仅是阅读方式是 LTR(Left-to-Right),如无特殊声明,接下来的文档不会因阅读方式(即 CSS 的 writing-mode
、 direction
和 HTML 的 dir
属性)列出不同的示意图。
除非需要显式的修改主轴方向,才需要在 Flex 容器上显式设置 flex-direction
, 比如像下图这种排版本方式:
row-reverse
和默认值 row
表现恰恰相反,适用于下面这样布局场景:
在 Flexbox 布局中, flex-direction
在指定 Flex 容器主轴方向时,也会对 Flex 项目的排列顺序有影响(在不改变 DOM 结构,要实现反方向排版时,非常适合)。除了 flex-direction
可以影响 Flex 项目排列顺序之外,在 Flex 项目中显式使用 order
属性也可以,并且可以在不影响 DOM 结构,按照你自己任意想要的意图进行排序。
目前在 imgcook 中使用 Flexbox 布局时,在 Flex 容器上都会显式的设置 flex-direction
的值,即使是默认值: row
。在布局算法优化中,可以做相应的处理,只有在非 row
时才在 Flex 容器上显式设置 flex-direction
:
控制 Flex 项目是否换行(Flex 行)
使用 flex-wrap
可以控制 Flex 项目在 Flex 容器换行的方式:
只有所有 Flex 项目宽度总和大于 Flex 容器主轴尺寸时,设置 flex-wrap
属性才能生效。
flex-wrap
取值为非 nowrap
(即 wrap
和 wrap-reverse
)都可以让 Flex 项目换行(列)显式,其中 wrap-reverse
表现行为和 wrap
刚好相反。
组合效果 | 表现效果 |
---|---|
效果 1 | |
效果 2 | |
效果 3 | |
效果 4 | |
效果 5 | |
效果 6 | |
效果 7 | |
效果 8 |
flex-direction
和 flex-wrap
可以简写成 flex-flow
。 flex-flow
使用时可以只显式设置一个值,也可以显式设置两个值:
flex-flow
只显式设置一个值,并且该值和<flex-direction>
相匹配时,flex-wrap
会取值initial
flex-flow
只显式设置一个值,并且该值和<flex-wrap>
相匹配时,flex-direction
会取值initial
flex-flow
显式设置两个值时,flex-direction
和flow-wrap
没有先后顺序之分,即可flex-flow: column wrap
和flex-flow: wrap column
等同
主轴方向对齐方式
在 Flex 容器中使用 justify-content
来控制 Flex 项目在 Flex 容器主轴方向的对齐方式,也可以用来分配 Flex 容器中主轴方向的剩余空间。使用 justify-content
分配 Flex 容器剩余空间,主要是将剩余空间按不同的对齐方式,将剩余空间分配给 Flex 项目的两侧,即控制 Flex 项目与 Flex 项目之间的间距。
justify-content
存在两个规范中:
在 Flexbox 布局模块中, justify-content
取值只要有以下六种:
需要注意 space-between
、 space-around
和 space-evenly
三者的差异:
space-between
会让第一个 Flex 项目的盒子起始边缘与 Flex 容器主轴起点相稳合,最后一个 Flex 项目的盒子结束边缘与 Flex 容器主轴终点相稳合,其它相邻 Flex 项目之间间距相等。当 Flex 容器中只有一个 Flex 项目时,其表现行为和flex-start
等同space-around
会让第一个 Flex 项目的盒子起始边缘与 Flex 容器主轴起点间距和最后一个 Flex 项目的盒子结束边缘与 Flex 容器主轴终点间距相等,并且等于其他相邻两个 Flex 项目之间间距的一半。当 Flex 容器中只有一个 Flex 项目时,其表现行为和center
等同space-evenly
会让第一个 Flex 项目的盒子起始边缘与 Flex 容器主轴起点间距和最后一个 Flex 项目的盒子结束边缘与 Flex 容器主轴终点间距相等,并且等于其他相邻两个 Flex 项目之间间距。当 Flex 容器中只有一个 Flex 项目时,其表现行为和center
等同
如果 Flex 容器没有额外的剩余空间,或者说剩余空间为负值时, justify-content
的值表现形式:
flex-start
会让 Flex 项目在 Flex 容器主轴结束点处溢出flex-end
会让 Flex 项目在 Flex 容器主轴起点处溢出center
会让 Flex 项目在 Flex 容器两端溢出space-between
和flex-start
相同space-around
和center
相同space-evenly
和center
相同
在 Flexbox 布局中,可以使用这些属性很好控制 Flex 容器的剩余空间,比如:
侧轴方向对齐方式
在 Flexbox 容器中使用 align-items
来控制 Flex 项目在侧轴方向的对齐方式。
align-items
的默认值是 stretch
,但只有 Flex 项目示显式设置 height
(或 width
) 值,Flex 项目才会被拉伸填满整个 Flex 容器。
如果 Flex 容器没有剩余空间或剩余空间为负值是:
flex-start
会让 Flex 项目在 Flex 容器侧轴终点处溢出flex-end
会让 Flex 项目在 Flex 容器侧轴起点处溢出center
会让 Flex 项目在 Flex 容器侧轴两侧溢出baseline
会让 Flex 项目在 Flex 容器侧轴终点溢出,有点类似于flex-start
多行(列)对齐方式
align-content
只适用于 Flex 容器在没有足够空间(所有 Flex 项目宽度之和大于 Flex 容器主轴尺寸),并且显式设置 flex-wrap
的值为非 wrap
时。
align-content
表现行为有点类似于 justify-cotent
控制 Flex 项目在主轴方向的对齐方式(分配 Flex 容器主轴剩余空间),而 align-content
可以用来控制多行状态下,行在 Flex 容器侧轴的对齐方式(分配 Flex 容器侧轴剩余空间)。可以把 align-content
状态下侧轴中的整行当作是 justify-content
状态下单个 Flex 项目。
align-content
还有一点不同之处,多了一个 stretch
值。当 Flex 容器中所有行的尺寸之和大于 Flex 容器侧轴尺寸(Flex 容器侧轴没有可用空间或可用空间为负值)时,各值表现行为:
flex-start
会让 Flex 容器的行在侧轴结束点溢出flex-end
会让 Flex 容器的行在侧轴起点溢出center
会让 Flex 容器行在侧轴两端溢出stretch
表现行为类似于flex-start
space-around
表现行为类似于center
space-between
表现行为类似于flex-start
space-evenly
表现行为类似于center
间距(行与行,列与列)
gap
用来控制 Flex 项目之间的间距,但会忽略 Flex 项目与 Flex 容器边缘的间距:
运用于 Flex 项目的属性
Flex 项目自身对齐方式
在 Flex 容器上可以使用 justify-content
、 align-content
以及 align-items
分配 Flex 容器主轴和侧轴的空间(控制 Flex 容器中所有 Flex 项目对齐方式)。如果你需要对 Flex 项目个体对齐方式做处理,可以使用 align-self
:
align-self
取不同值的效果:
Flex 项目的 align-self
显式设置值为 auto
时不会覆盖 Flex 容器的 align-items
; 另外如果在 Flex 项目上显式设置 margin
的值为 auto
时,Flex 项目的 align-self
值将会失效。
类似上图这样的场景, align-self
就非常实用。
Flex 项目排序
在 Flex 容器中使用 flex-direction
可以对 Flex 容器中的所有 Flex 项目按 “ LTR ”、“ RTL ”、“ TTB ” 或 “ BTT ” 方向排列。
- LTR :
flex-driection: row
- RTL :
flex-direction: row-reverse
- TTB :
flex-direction: column
- BTT :
flex-direction: column-reverse
在 Flex 项目上,还可以使用 order
指定具体的数值,在不改变 DOM 结构之下对 Flex 项目进行排序,其中数值越大,越在往后排:
在一些左右,上下互换顺序的时候,除了 flex-direction
之外,还可以在 Flex 项目设置 order
:
Flex 项目伸缩计算
Flex 项目中使用 flex
属性可以根据 Flex 容器的可用空间对自身做伸缩计算,其包含三个子属性: flex-basis
、 flex-shrink
和 flex-grow
。这几个属性都有其初始值:
flex-grow
的初始值为0
flex-shrink
的初始值为1
flex-basis
的初始值为auto
即 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
属性可以指定 1
个值(单值语法) 、 2
个值(双值语法) 或 3
个值(三值语法) 。
单值语法:值必须为以下其中之一:
- 一个无单位的数(
<number>
),比如flex: 1
,这个时候它会被当作<flex-grow>
的值 - 一个有效的宽度(
width
)值,比如flex: 30vw
,这个时候它会被当作<flex-basis>
的值 - 关键词
none
、auto
或initial
(即初始值)
双值语法:第一个值必须为一个无单位数值,并且它会被当作 <flex-grow>
的值;第二个值必须为以下之一:
- 一个无单位的数(
<number>
),它会被当作<flex-shrink>
的值 - 一个有效的宽度(
width
)值,它会被当作<flex-basis>
的值
三值语法:
- 第一个值必须是一个无单位数(
<number>
),并且它会被当作<flex-grow>
的值 - 第二个值必须是一个无单位数(
<number>
),并且它会被当作<flex-shrink>
的值 - 第三个值必须为一个有效的宽度(
width
)值,并且它会被当作<flex-basis>
的值
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-grow
计算
flex-grow
计算公式:
示例:
假设 Flex 容器中有四个 Flex 项目,具体参数:
- Flex 容器的宽度是
80vw
- Flex 容器中共有四个 Flex 项目,并且每个 Flex 项目的宽度是
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 公式中变量名称 | Flex1 | Flex2 | Flex3 | Flex4 | 总数 |
---|---|---|---|---|---|
Flex 项目的 flex-grow 值 | 0 | 1 | 2 | 3 | 0 + 1 + 2+ 3 = 6 |
Flex 项目宽度 | 10vw | 10vw | 10vw | 10vw | 10vw x 4 = 40vw |
Flex 容器宽度 | 80vw | ||||
Flex 容器剩余空间 | 80vw - 40vw = 40vw | ||||
Flex 项目新宽度 | ? | ? | ? | ? |
计算过程:
计算出来的结果:
flex-grow 公式中变量名称 | Flex1 | Flex2 | Flex3 | Flex4 | 总数 |
---|---|---|---|---|---|
Flex 项目的 flex-grow 值 | 0 | 1 | 2 | 3 | 0 + 1 + 2+ 3 = 6 |
Flex 项目宽度 | 10vw | 10vw | 10vw | 10vw | 10vw x 4 = 40vw |
Flex 容器宽度 | 80vw | ||||
Flex 容器剩余空间 | 80vw - 40vw = 40vw | ||||
Flex 项目新宽度 | 10vw | 16.667vw | 23.333vw | 30vw |
flex-grow
的取值还可以是 小数值 。如果将上面示例中的 flex-grow
的值分别换成 0
、 0.1
、 0.2
和 0.3
,这个时候 flex-grow
的总和(所有 Flex 项目的 flex-grow
和)就是 0.6
,该值小于 1
。这个时候,Flex 项目同样会根据 flex-grow
增长因子来瓜分 Flex 容器的剩余空间,Flex 自身宽度也会变大,但 Flex 容器的剩余空间不会被全部瓜分完,因为所有 flex-grow
和小于 1
。就该示例下,只瓜分了 Flex 容器剩余空间宽度的 60%
。
如果 flex-grow
和小于 1
, 其计算公式如下:
flex-grow 公式中变量名称 | Flex1 | Flex2 | Flex3 | Flex4 | 总数 |
---|---|---|---|---|---|
Flex 项目的 flex-grow 值 | 0 | .1 | .2 | .3 | 0 + 1 + 2+ 3 = .6 |
Flex 项目宽度 | 10vw | 10vw | 10vw | 10vw | 10vw x 4 = 40vw |
Flex 容器宽度 | 80vw | ||||
Flex 容器剩余空间 | 80vw - 40vw = 40vw | ||||
Flex 项目新宽度 | 10vw | 14vw | 18vw | 22vw |
即使 Flex 容器中所有 Flex 项目的 flex-grow
和大于 1
,但也不可以绝对地说,Flex 项目可以根据自身的 flex-grow
所占比率来瓜分 Flex 容器的剩余空间。因为元素的尺寸会受 max-width
的影响。当 Flex 项目显式设置了 max-width
的值时,当 Flex 项目根据flex-grow
计算出来的宽度大于 max-width
时,Flex 项目会按 max-width
的值为准。比如我们在前面的示例上,给所有 Flex 项目设置一个 max-width
的值为 18vw
,此时计算过程和结果如下:
flex-grow 公式中变量名称 | Flex1 | Flex2 | Flex3 | Flex4 | 总数 |
---|---|---|---|---|---|
Flex 项目的 flex-grow 值 | 0 | 1 | 2 | 3 | 0 + 1 + 2+ 3 = 6 |
Flex 项目宽度 | 10vw | 10vw | 10vw | 10vw | 10vw x 4 = 40vw |
Flex 容器宽度 | 80vw | ||||
Flex 容器剩余空间 | 80vw - 40vw = 40vw | ||||
Flex 项目计算出的新宽度 | 10vw | 16.667vw | 23.333vw | 30vw | |
Flex 项目设置最大宽度 | 18vw | 18vw | 18vw | 18vw | |
Flex 项目最终宽度 | 10vw | 16.667vw | 18vw | 18vw |
这个时候 Flex 容器剩余空间并没有全部用完, 40vw - 0vw - 6.667vw - 8vw - 8vw = 17.333vw
,即 Flex 容器还有 17.333vw
的剩余空间。
如果 Flex 项目没有显式设置与宽度有关的属性(包括 flex-basis
),那么 flex-grow
在计算时,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 项目的flex-grow
总和为4
将相应的值套用到 flex-grow
的公式中,可以得到:
flex-grow 公式中变量名称 | Flex1 | Flex2 | Flex3 | Flex4 | 总数 |
---|---|---|---|---|---|
Flex 项目的 flex-grow 值 | 1 | 1 | 1 | 1 | 1 x 4 = 4 |
Flex 项目宽度 | 43.36px | 92.09px | 148.83px | 189.56px | 465.84px |
Flex 容器宽度 | 804px | ||||
Flex 容器剩余空间 | 338.16px | ||||
Flex 项目新宽度 | 127.9px | 176.63px | 225.37px | 274.1px |
注意,不同的浏览器对小数处理有差异。
flex-shrink
计算
flex-shrink
计算公式:
示例:
假设 Flex 容器有四个 Flex 项目,具体参数如下:
- Flex 容器的宽度是
40vw
- Flex 容器中共有四个 Flex 项目,并且每个 Flex 项目的宽度都是
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 公式中变量名称 | Flex1 | Flex2 | Flex3 | Flex4 | 总数 |
---|---|---|---|---|---|
Flex 项目的 flex-shrink 值 | 0 | 1 | 2 | 3 | 0 + 1 + 2+ 3 = 6 |
Flex 项目宽度 | 15vw | 15vw | 15vw | 15vw | 15vw x 4 = 60vw |
Flex 容器宽度 | 40vw | ||||
Flex 容器不足空间 | 60vw - 40vw = 20vw | ||||
Flex 项目新宽度 | ? | ? | ? | ? |
计算过程:
计算出来的结果:
flex-shrink 公式中变量名称 | Flex1 | Flex2 | Flex3 | Flex4 | 总数 |
---|---|---|---|---|---|
Flex 项目的 flex-shrink 值 | 0 | 1 | 2 | 3 | 0 + 1 + 2+ 3 = 6 |
Flex 项目宽度 | 15vw | 15vw | 15vw | 15vw | 15vw x 4 = 60vw |
Flex 容器宽度 | 40vw | ||||
Flex 容器不足空间 | 60vw - 40vw = 20vw | ||||
Flex 项目收缩比例 | 0 | 0.1667 | 0.333 | 0.5 | |
Flex 项目新宽度 | 15vw | 11.67vw | 8.33vw | 5vw |
flex-shrink
的计算还可以甚至另一个公式来计算:
flex-shrink
和 flex-grow
类似,也可以取小数值。如果 Flex 容器中所有 Flex 项目的 flex-shrink
总和小于 1
,那么 Flex 容器的不足空间就不会被 Flex 项目按收缩因子瓜分完,Flex 项目会依旧会溢出 Flex 容器。
flex-shrink
总和小于 1
时,其计算公式如下:
基于上面的示例,把 Flex 项目的 flex-shrink
分别设置为 0
、 0.1
、 0.2
和 0.3
,计算过程如下:
flex-shrink 公式中变量名称 | Flex1 | Flex2 | Flex3 | Flex4 | 总数 |
---|---|---|---|---|---|
Flex 项目的 flex-shrink 值 | 0 | 0.1 | 0.2 | 0.3 | 0.6 |
Flex 项目宽度 | 15vw | 15vw | 15vw | 15vw | 15vw x 4 = 60vw |
Flex 容器宽度 | 40vw | ||||
Flex 容器不足空间 | 60vw - 40vw = 20vw | ||||
Flex 项目新宽度 | 15vw | 13vw | 11vw | 9vw |
即使 Flex 容器中所有 Flex 项目的 flex-shrink
和大于 1
,但也不可以绝对地说,Flex 项目可以根据自身的 flex-shrink
所占比率来瓜分 Flex 容器的不足空间。因为元素的尺寸会受 min-width
的影响。当 Flex 项目显式设置了 min-width
的值时,当 Flex 项目根据 flex-shrink
计算出来的宽度小于 min-width
时,Flex 项目会按 min-width
的值为准。比如我们在前面的示例上,给所有 Flex 项目设置一个 min-width
的值为 10vw
,此时计算过程和结果如下:
flex-shrink 公式中变量名称 | Flex1 | Flex2 | Flex3 | Flex4 | 总数 |
---|---|---|---|---|---|
Flex 项目的 flex-shrink 值 | 0 | 1 | 2 | 3 | 0 + 1 + 2+ 3 = 6 |
Flex 项目宽度 | 15vw | 15vw | 15vw | 15vw | 15vw x 4 = 60vw |
Flex 容器宽度 | 40vw | ||||
Flex 容器不足空间 | 60vw - 40vw = 20vw | ||||
Flex 项目收缩比例 | 0 | 0.1667 | 0.333 | 0.5 | |
Flex 项目设置最小宽度 | 10vw | 10vw | 10vw | 10vw | |
Flex 项目计算出的新宽度 | 15vw | 11.67vw | 8.33vw | 5vw | |
Flex 项目最终宽度 | 15vw | 11.67vw | 10vw | 10vw |
在这个情况之下,Flex 项目的最终宽度总和还是会大于 Flex 容器宽度,Flex 项目同样会溢出 Flex 容器。
flex-shrink
和 flex-grow
还有一点相似,那就是未显式给 Flex 容器的 Flex 项目显式设置与宽度有关的属性时,那么 Flex 项目的初始宽度会以其内容的宽度作为基准计算值。
flex-shrink
有一点和 flex-grow
完全不同,如果某个 Flex 项目按照 flex-shrink
计算出来的新宽度趋向于 0
时,Flex 项目将会按照该元素的 min-content
的大小来设置宽度,同时这个宽度将会转嫁到其他 Flex 项目,再按相应的收缩因子进行收缩。
比如我们将第四个 Flex 项目的 flex-shrink
的值从 3
改为 9
。根据上面提供的公式,可以获知,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 容器的对齐属性、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
计算
flex-basis
的计算相对于 flex-grow
和 flex-shrink
更略为复杂,因为它和 Flex 项目的 内容(Content) 、** width
、 min-width
和 max-width
都有关系。这里的关系指的就是它们之间的权重关系,简单地说,在 Flex 项目中同时出现这几个属性时,最终由谁来决定 Flex 项目的宽度**。
在 Flexbox 布局中,可以使用 flex-basis
来实始化 Flex 项目尺寸,即 在任何 Flex 容器空间(剩余空间或不足空间)分配发生之前初始化 Flex 项目尺寸。
事实上,在 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
) 属性限制。这一点必须得注意。
来看一个示例:<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
> .flex__container {
width: 600px;
display: flex;
border: 1px dashed #f36;
align-items: stretch;
}
Flex 项目不显式的设置任何与尺寸大小有关系属性,即用 content
来撑开 Flex 项目。<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 项目设置 flex-basis
属性,此时 flex-basis
会取默认值 auto
:
显式给 Flex 项目设置 width
值。:root {
--width: 120px;
}
.flex__item {
width: var(--width);
}
这个时候所有 Flex 项目宽度都是相等的:
浏览器计算出来的 flex-basis
值依旧为 auto
,但显式的设置了 width: 120px
,最终 width
属性的值决定了 Flex 项目的尺寸大小。
显式给 Flex 项目设置 flex-basis
值,即 Flex 项目同时有 width
和 flex-basis
值。: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
的值:
在 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
、 min-width
和 max-width
属性时,其权重计算遵循以下规则:
- 元素的
width
大于max-width
时,元素的width
等于max-width
,即max-width
能覆盖width
(max-width
胜出) - 元素的
width
小于min-width
时,元素的width
等于min-width
,即min-width
能覆盖width
(min-width
胜出) - 当
min-width
大于max-width
时,min-width
优先级将高于max-width
(min-width
胜出)
如果 Flex 项目同时出现 width
、 flex-basis
和 min-width
时,具体的运算过程如下:
- 根据法则:
content
➜width
➜flex-basis
,判断出运用于 Flex 项目的值,即flex-basis
会运用于 Flex 项目 (flex-basis
胜出) - 再根据法则:Flex 项目的
width
小于min-width
时,Flex 项目的width
等于min-width
,即min-width
能覆盖width
(**min-width
胜出**)
这样一来,如果 flex-basis
小于 min-width
时,Flex 项目的宽度会取值 min-width
,即 min-width
覆盖 flex-basis
(min-width
胜出)。
如果 Flex 项目同时出现 width
、 flex-basis
和 max-width
时,具体的运算过程如下:
- 根据法则:
content
➜width
➜flex-basis
,判断出运用于 Flex 项目的值,即flex-basis
会运用于 Flex 项目 (flex-basis
胜出) - 再根据法则:Flex 项目的
width
大于max-width
时,Flex 项目的width
等于max-width
,即max-width
能覆盖width
(max-width
胜出)
这样一来,如果 flex-basis
大于 max-width
时,Flex 项目的宽度会取值 max-width
,即 max-width
覆盖 flex-basis
( max-width
胜出)。
如果 Flex 项目同时出现 width
、 flex-basis
、 min-width
和 max-width
时,会在上面的规则上增加新的一条规则来进行判断:
**当
min-width
大于max-width
时,min-width
优先级将高于max-width
(min-width
胜出)**。
那么套用到 Flex 项目中:
flex-basis
大于max-width
,Flex 项目的宽度等于max-width
,即max-width
能覆盖flex-basis
(max-width
胜出)flex-basis
小于min-width
时,Flex 项目的宽度会取值min-width
,即min-width
覆盖flex-basis
(min-width
胜出 )
由于 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 项目上的
flex-shrink
和flex-grow
也会影响 Flex 项目尺寸大小!
如果你想更深入的了解 Flexbox 中 Flex 项目的计算,建议你花点时间阅读:
Flex 项目上的 margin
在 Flex 项目显式设置 margin
的值为 auto
可以灵活的控制单个 Flex 项目在 Flex 容器中的位置:
比如像下图这样的效果,使用 margin-left: auto
就非常的实用:
案例整理
padding 与自动宽问题
针对这个案例, 较好的方案对于内部元素不显式设置任何关于 padding
和 margin
的属性。人工实现可能会像下面这样:<div
class="flex__container"
> <span
class="coupon"
> 卷</span
> <span
class="divider"
> </span
> <span
class="price"
> ¥1000</span
> </div
> .flex__container {
display: inline-flex;
min-width: 200px;
height: 60px;
border: 1px solid rgba(255, 0, 54, 1);
background-color: rgba(255, 0, 54, 0.1);
border-radius: 4px;
color: #ff0036;
font-size: 24px;
font-weight: 400;
}
.flex__container > span {
display: inline-flex;
justify-content: center;
align-items: center;
}
.divider {
border-right: 1px dashed currentColor;
}
.coupon {
min-width: 50px;
}
.price {
flex: 1;
min-width: 0;
padding: 0 10px;
}
- ① 像类似 Button,Badge 等(外形看上去类似于内联块),设置 Flex 容器为
inline-flex
,并且给其设置一个min-width
(默认情况下等同于 Sketch 设计稿)和 一个height
- ② 从设计稿上分析可以得到,前面 “卷” 这个宽度是可知的,在该元素上设置一个固定宽度
width
- ③ 一个约
1px
的分割线,可以使用border
或者定死宽度width
- ④ 最右侧 “价格” 是下不可定因素,在 Flex 项目上,可以将其显式设置
flex: 1
,让该部分占用 Flex 容器的剩余空间 - ⑤ 为了让 “价格” 更具有扩展性,当其数值扩展到 Flex 容器无剩余空间时,数字会紧挨 Flex 容器主轴终点和分割线,为了让视觉上更友好,要以在 “价格” 容器设置一个
padding-left
和padding-right
小结
这篇笔记涉及到了 Flexbox 规范中的大部分内容以及一些临界点,在使用 Flexbox 来完成 UI 上的布局除了文章中提到的一些基础内容和细节之外,还有一些其他的东西。比如 Flex 容器中的定位,层级计算等,Flex 容器和 Flex 项目碰到overflow
以及 Flex 容器中的滚动计算等。这些对于场景具有较强的指定性,对于边界的处理也过于复杂。在我们平常使用 Flexbox 很少甚至不怎么会碰到。因此没有在文章中罗列。
如果你在使用 Flexbox,特别是在使用 imgcook 自动还原 UI,效果和你预期不一样,或者有不合理的地方,都可以随时来撩偶。
转载于:https://www.w3cplus.com/css/unknown-details-of-the-flexbox-layout.html