flex 弹性布局详解

flexbox by Hamza ERBAY on Feb 4, 2017

flexbox

CSS 弹性盒子布局是 CSS 的模块之一,定义了一种针对用户界面设计而优化的 CSS 盒子模型。在弹性布局模型中,弹性容器的子元素可以在任何方向上排布,也可以“弹性伸缩”其尺寸,既可以增加尺寸以填满未使用的空间,也可以收缩尺寸以避免父元素溢出。子元素的水平对齐和垂直对齐都能很方便的进行操控。通过嵌套这些框(水平框在垂直框内,或垂直框在水平框内)可以在两个维度上构建布局

怎么使用flexbox

通过设置 display 属性的值为 flexinline-flex 来定义弹性容器

1
2
3
4
5
6
7
8
9
10
.box {
display: flex;
outline: 1px solid grey;
}
.child {
width: 50px;
height: 50px;
background: red;
outline: 1px solid #000
}
1
2
3
4
5
<div class="box">
<div class="child">1</div>
<div class="child">2</div>
<div class="child">3</div>
</div>

理解 flexbox 的两根轴线

当使用 flex 布局时,首先想到的是两根轴线 — 主轴和交叉轴。主轴由 flex-direction定义,另一根交叉轴垂直于它。我们使用 flexbox 的所有属性都跟这两根轴线有关, 所以有必要在一开始首先理解它。

主轴

主轴由 flex-direction 定义,可以取4个值:

  • row
  • row-reverse
  • column
  • column-reverse

如果你选择了 row 或者 row-reverse,你的主轴是左右方向即横向。

flex

选择 column 或者 column-reverse 时,主轴会变成沿着上下方向延伸即纵向。

flex

交叉轴

交叉轴是垂直于主轴,所以如果你的flex-direction (主轴) 设成了 row 或者 row-reverse 的话,交叉轴的方向就是上下方向(纵向)。

flex

如果主轴方向设成了 column 或者 column-reverse,交叉轴就是横向。

flex

理解主轴和交叉轴的概念对于对齐 flexbox 里面的元素是很重要的;flexbox 的特性是沿着主轴或者交叉轴对齐之中的元素。

起始线和终止线

这里解释一下为什么不用左右来描述flex元素的方向。

因为在不同语言中,例如英语和阿拉伯文他们的书写方向是不一样的,过去,CSS的书写模式主要被认为是水平的,从左到右的。现代的布局方式涵盖了书写模式的范围,所以flex不再假设你是用什么语言。

下面在解释一下英语和阿拉伯文的书写顺序:

如果 flex-direction 是 row ,并且我是在书写英文,那么主轴的起始线是左边,终止线是右边。

flex

如果我在书写阿拉伯文,那么主轴的起始线是右边,终止线是左边。

flex

在这两种情况下,交叉轴的起始线是flex容器的顶部,终止线是底部,因为两种语言都是水平书写模式。

flex 容器

当添加display: flex || inline-flex,容器就变成flex容器,而它的子元素变成了flex元素,所有CSS属性都会有一个初始值,所以 flex 容器中的所有 flex 元素都会有下列默认行为:

  • 元素排列为一行 (flex-direction 属性的初始值是 row)。
  • 元素从主轴的起始线开始。
  • 元素不会在主维度方向拉伸,但是可是缩小。
  • 元素被拉伸来填充交叉轴大小。
  • flex-basis 属性为 auto。
  • flex-wrap 属性为 nowrap。

flex 容器属性

flex-direction

确立主轴

初始值:row

flex-direction: row; // 主轴为水平线,起始线在左,终止线在右
flex-direction row

flex-direction: row-reverse; // 主轴为水平线,起始线在右,终止线在左
flex-direction row-reverse

flex-direction: column; // 主轴为垂直线, 起始线在上,终止线在下
flex-direction column

flex-direction: column-reverse; // 主轴为垂直线,起始线在下,终止线在上
flex-direction column-reverse

请注意,dir 属性会把 row 和 row-reverse 的 flex 容器的起始线和终止线的影响。 如果它的 dir 属性是 rtl,row 表示起始线在右,终止线在左,而 row-reverse 表示起始线在左,终止线在右;

flex-wrap

指定 flex 元素单行显示还是多行显示

初始值:nowrap

flex-wrap: nowrap // flex元素被摆放到到一行,这可能导致溢出 flex 容器
flex-wrap: wrap // flex元素被打断到多个行中
flex-wrap: wrap-reverse // 和wrap值一样,但是 交叉轴的start 和 交叉轴的end 互换。

flex-flow

初始值: row nowrap;

CSS flex-flow 属性是 flex-direction 和 flex-wrap 的简写。

justify-content

定义主轴如何排布。

初始值:flex-start

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* 位置对齐 */
justify-content: center; /* 居中排列 */
justify-content: flex-start; /* 从主轴的start开始 */
justify-content: flex-end; /* 从主轴的end开始 */
justify-content: start;
justify-content: end;
justify-content: left; /* Pack items from the left */
justify-content: right; /* Pack items from the right */

/* 分布式对齐 */
justify-content: space-between; /* 均匀排列每个元素
首个元素放置于起点,末尾元素放置于终点 */
justify-content: space-around; /* 均匀排列每个元素
每个元素周围分配相同的空间 */
justify-content: space-evenly; /* 均匀排列每个元素
每个元素周围有相同的空间 */
justify-content: stretch; /* 均匀排列每个元素
拉伸‘auto’大小的元素以适应容器 */

/* 溢出对齐 */
justify-content: safe center;
justify-content: unsafe center;

当 length 属性和自动外边距属性(margin: auto)生效之后,对齐已经完成了。也就是说,如果存在至少一个弹性元素,而且这个元素的 flex-grow 属性不等于 0,那么对齐方式不会生效,就像没有多余空间的情况

align-items

定义交叉轴如何排布。

初始值:stretch

align-items属性以与justify-content相同的方式在侧轴方向上将当前行上的弹性元素对齐。

1
2
3
4
5
6
7
8

align-items: stretch; /* 弹性元素被在交叉轴方向被拉伸到与容器相同的高度或宽度。*/

align-items: center; /* 元素在交叉轴轴居中。如果元素在侧轴上的高度高于其容器,那么在两个方向上溢出距离相同 */

align-items: flex-start; /* 从交叉轴的start开始 */
align-items: flex-end; /* 从交叉轴的end开始 */
align-items: baseline; /* 对齐与基准线 */

align-content

对交叉轴中围绕flex元素的盒子进行分配空间

初始值:stretch

该属性对单行弹性盒子模型无效。

1
2
3
4
5
6
7
8
9
align-content: stretch;

align-content: center;

align-content: space-between;

align-content: space-around;

align-content: space-evenly;

align-items 与 align-content 区别

与align-content属性的区别在于align-items指定了当前Flex容器的每行项目的对齐方式,而align-content则指定了整体的对齐方式

flex 元素

flex 元素属性

order

规定了flex容器中的flex元素的顺序,元素按照 order 属性的值从小到大的顺序进行布局。拥有相同 order 属性值的元素按照它们在源代码中出现的顺序进行布局。

初始值:0

align-self

定义了单个弹性项目在交叉轴上应当如何对齐,这个定义会覆盖由 align-items 所确立的默认值。
如果任何 flex 元素的侧轴方向 margin 值设置为 auto,则会忽略 align-self。

初始值: auto

1
2
3
4
5
6
7
8
align-self: auto // 设置为父元素的 align-items 值,如果该元素没有父元素的话,就设置为 stretch。
align-self: flex-start // flex 元素会对齐到 交叉轴 的首端。
align-self: flex-end // flex 元素会对齐到 交叉轴 的尾端。
align-self: center // flex 元素会对齐到 交叉轴 的中间,如果该元素的 cross-size 的尺寸大于 flex 容器,将在两个方向均等溢出。
align-self: baseline // 所有的 flex 元素会沿着基线对齐
align-self: stretch // flex 元素将会基于容器的宽和高,按照自身 margin box 的 cross-size 拉伸。

align-self: inherit

flex-grow

flex-grow
可理解成flex元素占据主轴剩余空间的比例。
拉伸的结果是:原始宽度 + 占据剩余宽度

若flex容器width为400px,容器内有3个flex元素width为100px,这时
flex-grow1

当对第三个元素添加flex-grow:1,此元素width会扩展剩余的空间,因为其他两个元素默认flex-grow为0

1
2
3
0 + 0 + 1 = 1;
1/1 = 1 * 100px(剩余空间) = 100px;
100px + 100px = 200px

,第三个元素占100%比例(占用剩余的空间比例),最终结果为200px

flex-grow2

若把第二个元素也添加flex-grow:1,此时

1
2
3
4
0 + 1 + 1 = 2; 
1/2 = 0.5
0.5 * 100 = 50px
100px + 50px = 150px

,第二、三元素各占剩余空间的50%宽度。最终结果各为150px
flex-grow3

flex-shrink

flex-shrink:属性指定了 flex 元素的收缩规则。

flex 元素仅在默认宽度之和大于容器的时候才会发生收缩,其收缩的大小是依据 flex-shrink 的值。

假如flex容器是400px,现在有5个子元素分别都是100px如下面算法:

每个元素flex-shrink默认为1:

5 * 100 = 500 - 400 = 多出100 / 5 == 每个减20px

如果把其中一个元素改成flex-shrink:2

5 * 100 = 500 - 400 = 多出100 / 6 == 16.666666666,你为2的要减16.666666666 * 2

flex-shrink: 0 时,flex元素不会收缩,所以可能会出现超出容器。

flex-basis

flex-basis:指定了 flex 元素在主轴方向上的初始大小。
如果不使用 box-sizing 来改变盒模型的话,那么这个属性就决定了 flex 元素的内容盒(content-box)的宽或者高(取决于主轴的方向)的尺寸大小。

简单说与width属性类似,如果同时设置flex-basis和width,那么width属性会被覆盖掉。

1
2
3
flex-basis: 10em;
flex-basis: 3px;
flex-basis: auto;

弹性盒子相关属性

不影响弹性盒子的属性

由于弹性盒子使用了不同的布局算法,某些属性用在弹性容器上没有意义:

  • 多栏布局模块的 column-* 属性对弹性项目无效。
  • float 与 clear 对弹性项目无效。使用 float 将使元素的 display 属性计为block。
  • vertical-align 对弹性项目的对齐无效。

参考:
ZhoonChen
mdn