🧩 Flex
📖 概述
在CSS3规范中,Flexible Box Layout
模块被引入。Flexible Box模型也通常被称为Flexbox(弹性盒),它是一种一维的布局模型。在该布局模型中,弹性盒为其子元素提供了强大的空间分布和对齐能力。
将一个元素设置为弹性盒的CSS方式如下所示。
.element{
display: flex;
}
在Flexbox布局模型中,被设置为弹性盒的元素被称为弹性容器(Flex Container),而其子元素被称为弹性项目(Flex Item)。为了便于讲述,以下内容当中的弹性容器统称为容器,弹性项目统称为项目。
项目和容器都是相对概念,对于同一元素来说,它既可以作为其它元素的容器,也可以作为其它元素的项目。例如,在以下给定的元素层级中,元素A和B都被设置为了弹性盒。而元素B是元素A的项目,也是元素C的容器。
<div class="A" style="display: flex">
<div class="B" style="display: flex">
<div class="C"></div>
</div>
</div>
➖ 主轴与交叉轴
在容器中,所有项目都会按照容器的主轴和交叉轴进行分布。其中,主轴和交叉轴是两条参考轴线。
主轴(main axis):项目是按照容器的主轴方向进行排列的,排列方向永远是从起始端(main start)到结束端(main end)。
交叉轴(cross axis):项目在容器的交叉轴方向上可以选择是否换行,换行的方向永远是从起始端(cross start)到结束端(cross end)。交叉轴是与主轴垂直的轴,它与主轴一起构成了弹性容器的二维空间。
轴方向
flex布局中不能通过更改属性的方式直接调整交叉轴的方向,其取决于主轴的方向,若主轴是水平的则交叉轴的方向为垂直向下,若主轴是垂直的则交叉轴的方向是水平向右。
主轴方向可以通过容器属性flex-direction
,其有以下常用值。
- row: 缺省值,水平向右
- row-reverse: 水平向左
- column: 垂直向下
- column-reverse: 垂直向上
换行
行边界在哪?
在主轴上有不同高度的项目,且需要换行时(也有可能是一列,这取决于主轴方向,以下统称为行),每一行的边界是按照什么计算的呢?其实,在flex布局中,一行的末尾是排列在其之上的最高项目的底部。例如,在下面的布局中,第一行的末尾是项目A的底部,第二行的末尾是项目D的底部。
换行方式
当项目超出容器中一行的宽度时,换行方式便可以决定超出的项目将如何布局,也决定了所有项目在容器中是多行还是单行。通过容器属性flex-wrap
可调整换行方式,其有以下几种取值。
tip: 换行方式并不会更改项目在主轴上的排列顺序。在下面的布局演示请关注项目如何沿着交叉轴换行。
- nowrap: 缺省值,不换行。一般情况会按比列收缩主轴上的所有项目的宽度,使它们都能够在主轴的一行上排列(这是由
flex-shrink
决定的,将在下文介绍)。
- wrap: 换行,项目以原本的宽度沿着交叉轴方向换行。
- wrap-reverse: 同wrap,反向换行,此时交叉轴方向也会随之反转。
换行后的空隙问题:换行后若交叉轴方向还有剩余空间,则会在交叉轴方向产生间隙,使项目不那么拥挤。当然,这也会受交叉轴对齐方式的影响,将在下文介绍。
flex-flow
flex-flow
是flex-direction
和flex-wrap
两者的复合属性,其存在以下两种语法。
- 同时写两者,顺序不受限制,如下所示。
flex-flow: row wrap;
- 只写单个,忽略的属性会取缺省值,如下所示。
flex-flow: wrap
主轴对齐方式
主轴对齐方式指如何分配顺着容器主轴的项目之间及其周围的空间。主轴上的对齐方式由容器属性justify-content
控制,其有以下常用值。
tip: 主轴对齐方式也不会改变项目在主轴上的排列顺序。在下面的布局演示中未设置交叉轴对齐方式,但设置了换行方式为wrap,请关注不同行的项目在主轴上的分布。
- flex-start: 缺省值,项目靠主轴的起始端(main start)对齐。
- flex-end: 项目靠主轴的结束端(main end)对齐。
- center: 项目靠主轴居中对齐。
- space-around: 项目均匀分布,项目间间距相等且是离两端距离的二倍。
- space-between: 项目均匀分布,两端项目紧靠起始端和结束端,项目间间距相等。
- space-evenly: 项目均匀分布,项目间间距和离两端距离都相等。
交叉轴对齐方式
交叉轴对齐方式指如何分配顺着容器交叉轴的项目之间及其周围的空间,其分为单行和多行两种情况,下面将分别介绍。
tip: 上述中提到,当换行方式为wrap-reverse时,cross-start和cross-end会对调,这会影响交叉轴对齐方式。
单行
项目在交叉轴上只有单行时,交叉轴对齐方式由容器属性align-items
控制,其有以下常用值。
tip: 在下面的布局演示中的主轴对齐方式为缺省值flex-start,又由于是单行则设置换行方式为nowrap且只保留项目A,B,C(三者宽度并不会超出容器,其实换行方式可以设置为wrap),请关注单行项目在交叉轴上的对齐方式。
- flex-start: 单行项目靠交叉轴的起始端(cross start)对齐。
- flex-end: 单行项目靠交叉轴的结束端(cross end)对齐。
- center: 单行项目靠交叉轴居中对齐。
- baseline: 单行项目在基线上对齐,基线指项目中的文字基线。以下将项目A,B,C中的文字大小进行了调整,并都使用了微软雅黑,可以看到项目在交叉轴方向是沿着水平那条文字基线对齐的。
- stretch: 缺省值,当单行中的项目在交叉轴上未设置长度时,项目将拉伸占满整个交叉轴。这个长度取决于交叉轴的方向,若交叉轴为垂直则为高度,交叉轴为水平则为宽度。需要注意的是,设置长度为0px也会被认为已设置,并不会拉伸。以下将项目A和C的高度注释,且交叉轴方向为垂直,所以项目A和C都被拉伸填充满容器的整个高度。
多行
项目在交叉轴上存在多行时,交叉轴对齐方式由容器属性align-content
控制,其有以下常用值。
tip: 在下面的布局演示中的主轴对齐方式为缺省值flex-start,又由于是多行则设置换行方式为wrap,请关注多行项目在交叉轴上的对齐方式。
- flex-start: 多行项目靠交叉轴起始端(cross start)对齐。
- flex-end: 多行项目靠交叉轴结束端(cross end)对齐。
- center: 多行项目靠交叉轴居中对齐。
- space-around: 每行均匀分布,行间距相等且是离两端距离的二倍。
- space-between: 每行均匀分布,两端行仅靠起始端和结束端,行间距相等。
- space-evenly: 每行均匀分布,行间距和离两端距离都相等。
- stretch: 缺省值,若行中有项目未设置长度时,其将拉伸填充默认布局中的空隙,这个长度也取决于交叉轴的方向,若交叉轴为垂直则为高度,交叉轴为水平则为宽度,需要注意的是,设置长度为0px也会被认为是已设置,并不会拉伸。这个属性是根据剩余空间分配的,同时也跟行高
line-height
有关。以下将所有的项目的高度和行高注释,且交叉轴为垂直,所以每行在交叉轴上平均分配容器的高度,这也是最常用的情况之一。
水平垂直居中
通过flex布局能够轻松地实现某一元素在其父元素内容区中水平垂直居中。具体可以通过以下两种方式实现。
tip: 若父元素的宽度没有指定,flex布局也无法实现子元素在垂直方向上居中。
- 父元素开启flex布局,并把其主轴和交叉轴的对齐方式都设置为居中。
/* 父元素 */
display: flex;
justify-content: center;
align-items: center;
- 父元素开启flex布局,并将子元素的margin设置为自动。
/* 父元素 */
display: flex;
/* 子元素 */
margin: auto;
伸缩性
弹性盒子的“弹性“指的就是伸缩性,其是指项目在可用空间内调整自身大小的能力。
flex-basis
项目属性flex-basis
能控制其在主轴上的初始尺寸。在弹性盒子中,所有的布局计算都是根据这个属性作基准的,其有以下几种取值。
- auto: 缺省值,表示与项目宽度(或高度,按照主轴方向取)保持一致。
- 百分比:相对于容器的主轴长度进行计算。
- 像素值。
flex-grow
项目属性flex-grow
能控制其在主轴上的增长因子,对应伸缩性该词中的”伸“。也就是说,当一行项目在主轴上排列时,若该行中还存在剩余空间,容器会根据该属性给各个项目分配额外的空间。
flex-grow
接受非负整数作为值,缺省值为0,表示当前项目不会在剩余空间分配中获得额外的空间。
计算公式:
- 比例 =
current flex-grow / sum(flex-)
- 增长空间 = 比例 * 剩余空间
tip: 分配每行的剩余空间是在换行操作之后进行的。
flex-shrink
项目属性flex-grow
能控制其在主轴上的收缩因子,对应伸缩性该词中的”缩“。也就是说,当一行项目在主轴上排列时,若该行已无法正常容纳所有项目时,容器会根据该属性收缩各个项目
flex-grow
接受非负整数作为值,缺省值为1,表示容器在主轴上无法容纳所有项目时,当前项目会收缩,收缩的比例参照flex-grow
为1。
计算公式:
比例 =
(current flex-basis * current flex-shrink) / sum(flex-basis * flex-shrink)
收缩空间 = 比例 * 多余空间
tip:
- 项目收缩是在单行的情况才会出现,当换行方式为**wrap | wrap-reverse **时,容器永远不会收缩项目,即
flex-shrink
无用。- 当
flex-shrink
为0时,若容器在主轴上无法容纳项目时,且此时换行方式是nowrap
时,会造出溢出现象。
flex
flex
是flex-grow
、flex-shrink
和flex-basis
三者的复合属性,存在顺序限制,顺序一般为:grow->shrink->basis,但其存在以下几种常见的简写方式。
auto: 等同于 1 1 auto。
1 1 0: 等同于1。
0 0 auto: 等同于none。
0 1 auto: 等同于0 auto(即flex初始值)。
单独对齐
通过项目属性align-self
可单独调整某个项目在交叉轴上的对齐方式,一般应用于单行情况,与align-items
配合使用。
align-self
的取值与align-items
相同,唯一的区别为align-self
增加了一个auto的取值,表示与容器的align-items
相同,同时这也是一个缺省值。
排列顺序
前面说到,项目是沿着容器的主轴进行排列的。通过项目属性order
可以调整项目的排列顺序,取值为整数,缺省值为0。当项目的order
值相同时,会根据它们在HTML结构中的顺序进行排列,order
值越小其越先排列。
tip:
order
只会改变项目在主轴上的排列顺序,但不会改变元素在文档流中的顺序。
常见问题
- 当图片
img
作为项目后,为什么可能会造成自动按比例放大的现象?
例如,在以下效果中,用户图大小原本为128px * 128px,但当其作为粉色容器的项目时,图片大小会按比例放大至300px * 300px,容器大小为400px * 300px,主轴方向为row,换行方式为nowrap,且未显式地指定主轴和交叉轴的对齐方式。
造成这个现象的原因为:一般在设置图片img
时不会显式地指定宽高,而是依赖图片自身的大小。当图片作为项目时,若不指定其容器的交叉轴对齐方式,默认为stretch,而此时并未指定图片在交叉轴上的长度,所以图片的高度会占满整个容器的交叉轴,即粉色容器的高度300px,而图片会自适应调整大小,其宽度也随比例放大了。
解决方案为将换行方式设置为wrap,但此时图片的高度还会占满整个容器的交叉轴,只是宽度不会自适应更改,这显然不是最佳解决方案。最佳解决方案为更改交叉轴对齐方式,即容器属性justify-items
。