Skip to main content

flex布局

主轴与交叉轴

学习 flex 布局需要明白 主轴(main axis)交叉轴(cross axis)两个概念。首先采用了 flex 布局的元素被称为 "容器"(flex container),它的所有子元素都是容器的 "项目"(flex item),容器中存在两个轴:

  • 水平的主轴(main axis),主轴开始位置叫 main start,结束位置叫 main end,默认情况下是 从左往右
  • 垂直的交叉轴(cross axis),交叉轴开始位置 cross start,结束位置叫 cross end,默认情况下是 从上往下

pic.nm

如何成为 flex 容器

如果要让一个盒子成为 flex 容器,你需要调整其 display 属性:

  • display: flex,盒子具有 容器级块元素 特点,并且成为了 flex 容器,可以使用 flex 容器所有属性
  • display: inline-flex,盒子具有 行内块元素 特点,并且成为了 flex 容器,可以使用 flex 容器所有属性

flex 容器中项目的特点

无论项目是 块级元素 还是 行内元素,在 flex 容器中,盒子模型的所有属性对项目都有效,比如 span 元素是可以使用 width 和 height 的,并且垂直方向上的 margin 和 padding 也有效。

还需要注意的是,项目是按照 flex 容器的 主轴依次排列 的,从 main startmain end,默认情况下如果我们不给项目设置单独的 高度,那么该项目高度会占满整个 交叉轴范围,如果不设置单独的 宽度,那么项目的宽度由其内容规定。

important

关于项目在 flex 容器中的占位问题,具体需要结合容器的 flex-directionalign-items 而定,即主轴方向和项目在交叉轴上的对齐方式,具体在 flex-direcitonalign-items 会谈到。

<div class="container">
<div class="item">item</div>
<div class="item">item</div>
<div class="item">item</div>
<span class="item">item</span>
<span class="item">item</span>
</div>
.container {
width: 500px;
height: 500px;
border: solid;
display: flex;

.item {
border: solid;
}
}

pic.sm

另外,flex 布局也解决了许多标准文档流中存在的一些问题,比如 margin 垂直方向的塌陷问题、margin: auto 只能进行水平居中的问题。

容器的属性(9 个)

属性简述
flex-direction规定容器主轴的方向
flex-wrap规定容器中的项目是否换行
flex-flowflex-direcitonflex-wrap 的简写
justify-content规定项目在主轴上的对齐方式
align-items规定项目在交叉轴上的对齐方式
align-content规定多根轴线在交叉轴上的对齐方式
row-gap规定项目与项目之间的最小行间隔
column-gap规定项目与项目之间的最小列间隔
gaprow-gapcolumn-gap 的简写

flex-direction

该属性规定容器内主轴的方向,即项目的排列方向。

flex-direction: row | row-reverse | column | column-reverse;
  • row(默认值):主轴水平从左往右,交叉轴从上往下
  • row-reverse:主轴水平从右往左,交叉轴从上往下
  • column:主轴垂直从上往下,交叉轴从左往右
  • column-reverse:主轴垂直从下往上,交叉轴从左往右

这里具体来讨论一下容器中项目的占位问题,前文中说到,默认情况下,不给项目设置高度,那么项目的高度会占满整个 交叉轴范围,不给项目设置宽度,那么项目的宽度由其内容而定。

这个实际上这与 主轴 的是水平还是垂直有关:

  • 主轴水平,项目的水平占位就由内容规定,垂直占位则占满交叉轴
  • 主轴垂直,项目的垂直占位就由内容规定,水平占位则占满交叉轴

flex-wrap

该属性规定容器内的项目是否换行,默认情况下,项目都排在一条线上,就算超出了容器范围也不会主动换行。

flex-wrap: nowrap | wrap | wrap-reverse;
  • nowrap(默认值):不换行
  • wrap:换行,第一行在上方,新起的行在第一行下面从上往下排列
  • wrap-reverse:反向换行,新起的行从第一行上面从下往上排列(此时第一行会位于交叉轴的末端)

这里注意一下 nowrap 默认值,它会让项目全部依次排列在一条线上,并且当我们继续添加项目时如果会超出容器范围,那么此时会挤压所有项目的占位(平均挤压),就算项目设置了固定的占位大小(width or height),也会被挤压到不等于设置的占位大小,挤压的最大限度就是项目占位由其内容规定,此时如果继续添加项目,就会导致项目溢出容器范围。

我们来看下面这个例子:

.container {
width: 500px;
height: 500px;
border: solid;
display: flex;

.item {
width: 100px;
height: 100px;
border: solid;
}
}

pic.nm

这里可以看见,前面内容为 item 的项目已经的宽度已经由其内容(小于 100px)规定了,此时我们继续添加项目。

pic.nm

最终这个无内容的项目只剩下 border 占位,然后超出容器范围。

如果容器设置为 flex-wrap: wrap ,那么当一行的项目放不下时,就会自动换行。

pic.sm

需要注意的是,这时换行的项目并不会紧挨着上一行的项目另起一行进行排列(因为这里的项目指定了固定占位大小),这和 align-content 属性有关,现在只需要知道有这个行为即可。

pic.sm

如果换行过多,也会导致项目从交叉轴方向溢出。

pic.sm

flex-flow

该属性是 flex-directionflex-wrap 的简写形式。

flex-flow: <flex-direction> | <flex-wrap>;

默认值为 flex-flow: row nowrap

justify-content

该属性规定了项目在主轴上的对齐方式。

justify-content: flex-start | flex-end | center | space-around | space-between |
space-evenly;
  • flex-start(默认值):往主轴 起始端 对齐
  • flex-end:往主轴 结束端 对齐
  • center:在主轴居中
  • space-around:每个项目两侧有相同间距 x,那么两个项目直接的间距为 2x,项目与容器边缘的间距为 x
  • space-between:项目在主轴两端对齐(项目与容器边缘间距为 0),两个项目之间的距离相同
  • space-evenly:项目与项目之间的间距,还有项目与容器边缘的间距都一样

pic.sm

align-items

该属性规定了项目在交叉轴上的对齐方式。

align-items: stretch | flex-start | flex-end | center | baseline;
  • stretch(默认值):交叉轴垂直时,未指定项目的 height 或者 height 取值为 auto,则项目的高度占满整个容器,同理,交叉轴水平时,没有指定项目的 width,则项目的宽度占满整个容器
  • flex-start:项目在交叉轴的 起始端 对齐
  • flex-end:项目在交叉轴的 结束端 对齐
  • center:项目在交叉轴居中对齐
  • baseline:项目在交叉轴方向上以文字基线对齐,了解即可,这个并不常用

pic.sm

important

除了 align-items: stretch,其余的取值对于项目在交叉轴的占位都是由内容决定(前提是没有指定 width or height)。

align-content

该属性规定了 多根轴线 在交叉轴上的对齐方式,前提必须设置 flex-wrap: wrap | wrap-reverse,并且有多行项目,只有一行项目不会时,该属性不会生效。

这里解释一下,上面所说的 多根轴线 是什么意思,可以理解为 flex 容器中,项目换行以后,每一个项目所在排列在的一条直线上就可以看作一根轴线,这里的 多根轴线 就可以看作 多根平行的主轴,每根主轴都控制着该行项目的排列。

align-content: stretch | flex-start | flex-end | center | space-between |
space-around | space-evenly;
  • stretch(默认值):在 css-tricks 中也有说 normal 为默认值的,但是我自己测试了发现它们的行为都是一样的,它的作用是让轴线以 align-items: stretch 且项目没有设置交叉轴方向的占位时项目的行为而定位的,可以理解为轴线平分填充了交叉轴,说得很难理解,具体看下面展示。
  • flex-start
  • flex-end
  • center
  • space-between
  • space-around

当取值是默认的 stretch 时,多行项目是如下排列的:

pic.sm

里面的每一个项目都是有固定宽高的,且 align-itemsstretch,这里我们就来解释一下为什么新起行的项目之间会有一段空隙,这个行为实际上就是 align-content: stretch 的效果,现在我们把项目的高度设置为 auto,就会发现每一行项目拉伸,且平均占位了剩余的交叉轴空间:

pic

所以,align-content: stretch 的效果就是让轴线拉伸平均分布在交叉轴剩余空间。

其他属性取值的效果可以参考下面,它们的原理和主轴上项目的对齐方式一样,这里不过是换成了每一行项目在交叉轴上的对齐方式:

pic

important

关于 align-itemsalign-content,对于初学者来说并不是很好分别,实际上只需要讨论 flex 容器 具有多行项目 情况下:

  • align-items 只会把所有项目看做一个整体去调整它在交叉轴上的对齐方式,每行项目之间的间距等并不会去理会
  • align-content 是把每行的项目看作一个单位,它可以调整每行项目在交叉轴上的间距划分等对齐方式
  • 比如 align-content: center 会让每一行聚拢在交叉轴中部,此时每一行之间的间距为 0,但是 align-items: center 只会将所有项目看做整体在交叉轴上居中,每一行之间的间距依旧保持

gap-rowgap-column

这两个属性的作用是用来设置 flex 容器中项目与项目之间的最小限度间隙,请注意它不会调整项目和容器边缘之间的间隙。

gap-row: <length>;
gap-column: <length>;

在某些情况这个属性比直接调整项目的 margin 更有效。

另外,这两个属性也有简写形式:

.container {
display: flex;
...
gap: 10px;
gap: 10px 20px; /* row-gap column gap */
row-gap: 10px;
column-gap: 20px;
}
important

它不仅适用于 flex 布局,也适用于网格和多列布局

项目的属性(6 个)

属性简述
order规定当前项目的排列顺序
flex-grow规定 flex 容器中剩余空间的多少应该分配给该项目
flex-shrink规定该项目的收缩规则
flex-basis规定该项目在主轴方向上的初始大小
flexflex-growflex-shrinkflex-basis 的简写
align-self调整指定项目在交叉轴的对齐方式

order

该属性规定项目的排列顺序。数值越小,排列顺序离主轴和交叉轴的 起始端 越近,默认为 0,可以是负数。

order: <integer>;

这里需要注意一点的就是,当 flex 容器内有多行项目时,order 控制的项目的顺序是先沿主轴方向再沿交叉轴方向。

flex-grow

该属性可以规定项目占据 flex 容器主轴的剩余空间(优先级高于 width or height),不可为负数,默认值为 0,即项目不会自动增加占位。

flex-grow: <number>;

另外,flex-grow 也可以规定同一条主轴上的项目占位比例,比如一条主轴上有 4 个项目,它们的 flex-grow 的比值为 1:1:2:1,那么第 3 个项目在主轴的占位是其余项目的两倍。

flex-shrink

该属性规定了项目的收缩程度,一般用于主轴上所有项目的原始宽度加起来比容器大时,并且 flex-wrap: nowrap,此时就会挤压收缩主轴上的项目,默认值为 1,表示每一个项目都等比例收缩。

flex-shrink: <number>;

如果我们给主轴上的某个项目设置 flex-shrink: 0,那么主轴上就算放再多的项目也能够保证该项目不发生挤压收缩。

flex-basis

该属性规定了项目在主轴方向上分配剩余空间之前元素的默认大小,优先级高于自身的 width or height,默认值为 auto。

flex-basis: 0 | 100% | auto | <length>;

它的主要作用就是保证主轴方向变化后,项目在主轴上的占位也由 flex-basis 决定,这样可以保证项目在主轴上的占位保持不变。

当取默认值 auto 时,如果我们没有给项目设置宽度,那么项目的宽度是由内容大小决定的。

pic.nm

如果是 flex-basis: 0 | 0%,那么会以内容的一个单词、汉字的最大宽度作为项目宽度。

pic.nm

另外,对于百分比单位,计算值是相对于主轴的长度决定。

flex

该属性是 flex-growflex-shrinkflex-basis 的简写,默认值为 flex: 0 1 auto

flex: none | [ <flex-grow> <flex-shrink>? || <flex-basis> ];

第一个参数是 flex-grow,后面两个参数 flex-shrinkflex-basis 为可选参数。

建议使用该属性来控制项目的 flex-growflex-shrinkflex-basis,当我们给该属性一个值的时候:

.item {
flex: 1; /* 等价于 flex: 1 1 0% */
}

里面的 flex-shrink 默认为 1,flex-basis 会自动调整为 0%。

另外,该属性还可以直接取值为 auto

.item {
flex: auto; /* 等价于 flex: 1 1 auto */
}

align-self

该属性用于调整单个项目在交叉轴上的对齐方式,默认值为 auto,原理和 align-items 一致,区别就是前者用于调整单个项目在交叉轴上的对齐方式,后者则调整所有项目在交叉轴上的对齐方式。

align-self: auto | flex-start | flex-end | center | baseline | stretch;

取默认值 auto 的含义就是让项目在交叉轴上的对齐方式由 align-items 决定。

Reference