Skip to content

🧩 Flex

📖 概述

​ 在CSS3规范中,Flexible Box Layout模块被引入。Flexible Box模型也通常被称为Flexbox(弹性盒),它是一种一维的布局模型。在该布局模型中,弹性盒为其子元素提供了强大的空间分布和对齐能力。

​ 将一个元素设置为弹性盒的CSS方式如下所示。

css
.element{
    display: flex;
}

​ 在Flexbox布局模型中,被设置为弹性盒的元素被称为弹性容器(Flex Container),而其子元素被称为弹性项目(Flex Item)。为了便于讲述,以下内容当中的弹性容器统称为容器,弹性项目统称为项目

​ 项目和容器都是相对概念,对于同一元素来说,它既可以作为其它元素的容器,也可以作为其它元素的项目。例如,在以下给定的元素层级中,元素A和B都被设置为了弹性盒。而元素B是元素A的项目,也是元素C的容器。

html
<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,其有以下常用值。

  1. row: 缺省值,水平向右

主轴方向row

  1. row-reverse: 水平向左

主轴方向row-reverse

  1. column: 垂直向下

主轴方向column

  1. column-reverse: 垂直向上

主轴方向column-reverse

换行

行边界在哪?

​ 在主轴上有不同高度的项目,且需要换行时(也有可能是一列,这取决于主轴方向,以下统称为行),每一行的边界是按照什么计算的呢?其实,在flex布局中,一行的末尾是排列在其之上的最高项目的底部。例如,在下面的布局中,第一行的末尾是项目A的底部,第二行的末尾是项目D的底部。

行边界位置

换行方式

​ 当项目超出容器中一行的宽度时,换行方式便可以决定超出的项目将如何布局,也决定了所有项目在容器中是多行还是单行。通过容器属性flex-wrap可调整换行方式,其有以下几种取值。

tip: 换行方式并不会更改项目在主轴上的排列顺序。在下面的布局演示请关注项目如何沿着交叉轴换行。

  1. nowrap: 缺省值,不换行。一般情况会按比列收缩主轴上的所有项目的宽度,使它们都能够在主轴的一行上排列(这是由flex-shrink决定的,将在下文介绍)。

换行方式nowrap

  1. wrap: 换行,项目以原本的宽度沿着交叉轴方向换行。

换行方式wrap

  1. wrap-reverse: 同wrap,反向换行,此时交叉轴方向也会随之反转。

换行方式wrap-reverse

换行后的空隙问题:换行后若交叉轴方向还有剩余空间,则会在交叉轴方向产生间隙,使项目不那么拥挤。当然,这也会受交叉轴对齐方式的影响,将在下文介绍。

flex-flow

flex-flowflex-directionflex-wrap两者的复合属性,其存在以下两种语法。

  1. 同时写两者,顺序不受限制,如下所示。
css
flex-flow: row wrap;
  1. 只写单个,忽略的属性会取缺省值,如下所示。
css
flex-flow: wrap

主轴对齐方式

​ 主轴对齐方式指如何分配顺着容器主轴的项目之间及其周围的空间。主轴上的对齐方式由容器属性justify-content控制,其有以下常用值。

tip: 主轴对齐方式也不会改变项目在主轴上的排列顺序。在下面的布局演示中未设置交叉轴对齐方式,但设置了换行方式为wrap,请关注不同行的项目在主轴上的分布。

  1. flex-start: 缺省值,项目靠主轴的起始端(main start)对齐。

主轴对齐方式flex-start

  1. flex-end: 项目靠主轴的结束端(main end)对齐。

主轴对齐方式flex-end

  1. center: 项目靠主轴居中对齐。

主轴对齐方式center

  1. space-around: 项目均匀分布,项目间间距相等且是离两端距离的二倍。

主轴对齐方式space-around

  1. space-between: 项目均匀分布,两端项目紧靠起始端和结束端,项目间间距相等。

主轴对齐方式space-between

  1. space-evenly: 项目均匀分布,项目间间距和离两端距离都相等。

主轴对齐方式space-evenly

交叉轴对齐方式

​ 交叉轴对齐方式指如何分配顺着容器交叉轴的项目之间及其周围的空间,其分为单行和多行两种情况,下面将分别介绍。

tip: 上述中提到,当换行方式为wrap-reverse时,cross-start和cross-end会对调,这会影响交叉轴对齐方式。

单行

​ 项目在交叉轴上只有单行时,交叉轴对齐方式由容器属性align-items控制,其有以下常用值。

tip: 在下面的布局演示中的主轴对齐方式为缺省值flex-start,又由于是单行则设置换行方式为nowrap只保留项目A,B,C(三者宽度并不会超出容器,其实换行方式可以设置为wrap),请关注单行项目在交叉轴上的对齐方式。

  1. flex-start: 单行项目靠交叉轴的起始端(cross start)对齐。

交叉轴单行对齐方式flex-start

  1. flex-end: 单行项目靠交叉轴的结束端(cross end)对齐。

交叉轴单行对齐方式flex-end

  1. center: 单行项目靠交叉轴居中对齐。

交叉轴单行对齐方式center

  1. baseline: 单行项目在基线上对齐,基线指项目中的文字基线。以下将项目A,B,C中的文字大小进行了调整,并都使用了微软雅黑,可以看到项目在交叉轴方向是沿着水平那条文字基线对齐的。

交叉轴单行对齐方式baseline

  1. stretch: 缺省值,当单行中的项目在交叉轴上未设置长度时,项目将拉伸占满整个交叉轴。这个长度取决于交叉轴的方向,若交叉轴为垂直则为高度,交叉轴为水平则为宽度。需要注意的是,设置长度为0px也会被认为已设置,并不会拉伸。以下将项目A和C的高度注释,且交叉轴方向为垂直,所以项目A和C都被拉伸填充满容器的整个高度。

交叉轴单行对齐方式stretch

多行

​ 项目在交叉轴上存在多行时,交叉轴对齐方式由容器属性align-content控制,其有以下常用值。

tip: 在下面的布局演示中的主轴对齐方式为缺省值flex-start,又由于是多行则设置换行方式为wrap,请关注多行项目在交叉轴上的对齐方式。

  1. flex-start: 多行项目靠交叉轴起始端(cross start)对齐。

交叉轴多行对齐方式flex-start

  1. flex-end: 多行项目靠交叉轴结束端(cross end)对齐。

交叉轴多行对齐方式flex-end

  1. center: 多行项目靠交叉轴居中对齐。

交叉轴多行对齐方式center

  1. space-around: 每行均匀分布,行间距相等且是离两端距离的二倍。

交叉轴多行对齐方式space-around

  1. space-between: 每行均匀分布,两端行仅靠起始端和结束端,行间距相等。

交叉轴多行对齐方式space-between

  1. space-evenly: 每行均匀分布,行间距和离两端距离都相等。

交叉轴多行对齐方式space-evenly

  1. stretch: 缺省值,若行中有项目未设置长度时,其将拉伸填充默认布局中的空隙,这个长度也取决于交叉轴的方向,若交叉轴为垂直则为高度,交叉轴为水平则为宽度,需要注意的是,设置长度为0px也会被认为是已设置,并不会拉伸。这个属性是根据剩余空间分配的,同时也跟行高line-height有关。以下将所有的项目的高度和行高注释,且交叉轴为垂直,所以每行在交叉轴上平均分配容器的高度,这也是最常用的情况之一。

交叉轴多行对齐方式stretch

水平垂直居中

​ 通过flex布局能够轻松地实现某一元素在其父元素内容区中水平垂直居中。具体可以通过以下两种方式实现。

tip: 若父元素的宽度没有指定,flex布局也无法实现子元素在垂直方向上居中。

  1. 父元素开启flex布局,并把其主轴和交叉轴的对齐方式都设置为居中。
css
/* 父元素 */
display: flex;
justify-content: center;
align-items: center;
  1. 父元素开启flex布局,并将子元素的margin设置为自动。
css
/* 父元素 */
display: flex;

/* 子元素 */
margin: auto;

伸缩性

​ 弹性盒子的“弹性“指的就是伸缩性,其是指项目在可用空间内调整自身大小的能力。

flex-basis

​ 项目属性flex-basis能控制其在主轴上的初始尺寸。在弹性盒子中,所有的布局计算都是根据这个属性作基准的,其有以下几种取值。

  1. auto: 缺省值,表示与项目宽度(或高度,按照主轴方向取)保持一致。
  2. 百分比:相对于容器的主轴长度进行计算。
  3. 像素值。

flex-grow

​ 项目属性flex-grow能控制其在主轴上的增长因子,对应伸缩性该词中的”伸“。也就是说,当一行项目在主轴上排列时,若该行中还存在剩余空间,容器会根据该属性给各个项目分配额外的空间。

flex-grow接受非负整数作为值,缺省值为0,表示当前项目不会在剩余空间分配中获得额外的空间。

计算公式

  1. 比例 = current flex-grow / sum(flex-)
  2. 增长空间 = 比例 * 剩余空间

tip: 分配每行的剩余空间是在换行操作之后进行的。

flex-shrink

​ 项目属性flex-grow能控制其在主轴上的收缩因子,对应伸缩性该词中的”缩“。也就是说,当一行项目在主轴上排列时,若该行已无法正常容纳所有项目时,容器会根据该属性收缩各个项目

flex-grow接受非负整数作为值,缺省值为1,表示容器在主轴上无法容纳所有项目时,当前项目会收缩,收缩的比例参照flex-grow为1。

计算公式

  1. 比例 = (current flex-basis * current flex-shrink) / sum(flex-basis * flex-shrink)

  2. 收缩空间 = 比例 * 多余空间

tip:

  1. 项目收缩是在单行的情况才会出现,当换行方式为**wrap | wrap-reverse **时,容器永远不会收缩项目,即flex-shrink无用。
  2. flex-shrink为0时,若容器在主轴上无法容纳项目时,且此时换行方式是nowrap时,会造出溢出现象。

flex

flexflex-growflex-shrinkflex-basis三者的复合属性,存在顺序限制,顺序一般为:grow->shrink->basis,但其存在以下几种常见的简写方式。

  1. auto: 等同于 1 1 auto

  2. 1 1 0: 等同于1

  3. 0 0 auto: 等同于none

  4. 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只会改变项目在主轴上的排列顺序,但不会改变元素在文档流中的顺序。

常见问题

  1. 当图片img作为项目后,为什么可能会造成自动按比例放大的现象?

​ 例如,在以下效果中,用户图大小原本为128px * 128px,但当其作为粉色容器的项目时,图片大小会按比例放大至300px * 300px,容器大小为400px * 300px,主轴方向为row,换行方式为nowrap,且未显式地指定主轴和交叉轴的对齐方式。

图片自动缩放问题

​ 造成这个现象的原因为:一般在设置图片img时不会显式地指定宽高,而是依赖图片自身的大小。当图片作为项目时,若不指定其容器的交叉轴对齐方式,默认为stretch,而此时并未指定图片在交叉轴上的长度,所以图片的高度会占满整个容器的交叉轴,即粉色容器的高度300px,而图片会自适应调整大小,其宽度也随比例放大了。

​ 解决方案为将换行方式设置为wrap,但此时图片的高度还会占满整个容器的交叉轴,只是宽度不会自适应更改,这显然不是最佳解决方案。最佳解决方案为更改交叉轴对齐方式,即容器属性justify-items

上次更新于: