flex-wrap如何与align-self、align-items和align-content配合使用?

102

align-self

在下面的代码中,align-selfflex-wrap: nowrap 一起使用。

enter image description here

flex-container {
  display: flex;
  flex-wrap: nowrap;
  align-content: flex-start;
  width: 250px;
  height: 250px;
  background-color: silver;
}
flex-item {
  flex: 0 0 50px;
  height: 50px;
  margin: 5px;
  background-color: lightgreen;
}
flex-item:last-child {
  align-self: flex-end;
  background-color: crimson;
}
<flex-container>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
</flex-container>

但是当容器切换到 flex-wrap: wrap 时,align-self 属性失效。

enter image description here

flex-container {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  width: 250px;
  height: 250px;
  background-color: silver;
}
flex-item {
  flex: 0 0 50px;
  height: 50px;
  margin: 5px;
  background-color: lightgreen;
}
flex-item:last-child {
  align-self: flex-end;
  background-color: crimson;
}
<flex-container>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
</flex-container>


align-items

同样地,为什么在这里(禁用换行)align-items有效:

enter image description here

flex-container {
  display: flex;
  flex-wrap: nowrap;
  align-items: flex-end;
  align-content: flex-start;
  width: 250px;
  height: 250px;
  background-color: silver;
}
flex-item {
  flex: 0 0 50px;
  height: 50px;
  margin: 5px;
  background-color: lightgreen;
}
<flex-container>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
</flex-container>

...但不是在这里(启用了包装):

enter image description here

flex-container {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  align-content: flex-start;
  width: 250px;
  height: 250px;
  background-color: silver;
}

flex-item {
  flex: 0 0 50px;
  height: 50px;
  margin: 5px;
  background-color: lightgreen;
}
<flex-container>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
</flex-container>


align-content

当使用 flex-wrap: nowrap 时,align-content 属性将无法垂直居中弹性项目:

enter image description here

flex-container {
  display: flex;
  flex-wrap: nowrap;
  align-content: center;
  width: 250px;
  height: 250px;
  background-color: silver;
}

flex-item {
  flex: 0 0 50px;
  height: 50px;
  margin: 5px;
  background-color: lightgreen;
}
<flex-container>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
</flex-container>

但是,奇怪的是,如果启用了包裹并且省略了align-content,容器在这里创建行之间的宽间隙:

enter image description here

flex-container {
  display: flex;
  flex-wrap: wrap;
  /* align-content: center; */
  width: 250px;
  height: 250px;
  background-color: silver;
}

flex-item {
  flex: 0 0 50px;
  height: 50px;
  margin: 5px;
  background-color: lightgreen;
}
<flex-container>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
</flex-container>

而且 align-self 再次起作用了。

enter image description here

flex-container {
  display: flex;
  flex-wrap: wrap;
  /* align-content: center; */
  width: 250px;
  height: 250px;
  background-color: silver;
}
flex-item {
  flex: 0 0 50px;
  height: 50px;
  margin: 5px;
  background-color: lightgreen;
}

flex-item:nth-child(even) {
  align-self: flex-end;
  background-color: crimson;
}
<flex-container>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
</flex-container>

flex-wrap 如何与 align-selfalign-itemsalign-content 协同工作?

2个回答

210

简短回答

flex-wrap属性看起来很基础——它控制弹性项目是否可以换行,但实际上它对整个弹性盒布局产生了广泛的影响。

flex-wrap属性决定你将使用的弹性容器的类型。

  • flex-wrap: nowrap创建一个单行弹性容器
  • flex-wrap: wrapwrap-reverse创建一个多行弹性容器

align-itemsalign-self属性在单行和多行容器中均可使用。然而,它们只有在弹性线的交叉轴上有空闲空间时才能起作用。

align-content属性仅在多行容器中起作用,在单行容器中被忽略。


解释

flexbox specification提供了四个关键字属性来对齐弹性项目:

  • align-items
  • align-self
  • align-content
  • justify-content

要理解这些属性的功能,首先需要了解弹性容器的结构。


第一部分:理解Flex容器的主轴和交叉轴

X轴和Y轴

Flex容器在两个方向上工作:x轴(水平)和y轴(垂直)。

x-axis and the y-axis

来源:维基百科

flex 容器的子元素,也称为“flex 项目”,可以在任何方向上对齐。(忽略上面图片中的 z 轴。这里不适用。)

这是 flex 对齐的最基本层次。

主轴和交叉轴

在 flex 布局中,覆盖 x 和 y 轴的是 主轴交叉轴

默认情况下,主轴是水平的(x 轴),交叉轴是垂直的(y 轴)。这是根据 flexbox 规范 定义的初始设置。

flex main axis and cross axis

                                                                                                                来源: W3C

然而,与固定的 x 和 y 轴不同,主轴和交叉轴可以交换方向。

flex-direction 属性

在上面的图像中,主轴是水平的,交叉轴是垂直的。如前所述,这是 flex 容器的初始设置。

然而,这些方向可以通过 flex-direction 属性轻松地进行切换。该属性控制主轴的方向;它确定 flex 项目是垂直还是水平对齐。

来自规范:

5.1. 弹性流方向:flex-direction属性

flex-direction属性指定了弹性容器中的伸缩项如何放置,通过设置弹性容器主轴的方向来确定伸缩项的布局方向。

flex-direction属性有四个值:

/* main axis is horizontal, cross axis is vertical */
flex-direction: row; /* default */
flex-direction: row-reverse;

/* main axis is vertical, cross axis is horizontal */    
flex-direction: column;
flex-direction: column-reverse;

交叉轴始终与主轴垂直。

第二部分:弹性线

在容器内,弹性项存在于一条线上,称为“弹性线”。

弹性线可以是行或列,具体取决于flex-direction属性。

一个容器可以有一个或多个弹性线,具体取决于flex-wrap属性。

单行弹性容器

flex-wrap: nowrap建立了一个单行弹性容器,在这个容器中,弹性项被强制保持在一行内(即使它们溢出容器)。

a single-line flex container (one column)

上面的图像有一条弹性线。

flex-container {
  display: flex;
  flex-direction: column;
  flex-wrap: nowrap; /* <-- allows single-line flex container */
  width: 250px;
  height: 250px;
  background-color: silver;
}
flex-item {
  flex: 0 0 50px;
  width: 50px;
  margin: 5px;
  background-color: lightgreen;
}
<flex-container>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
</flex-container>

多行弹性容器

flex-wrap: wrapwrap-reverse 建立一个多行弹性容器,其中弹性项可以创建新的行。

multi-line flex container (3 columns)

上面的图片有三个弹性线。

flex-container {
  display: flex;
  flex-direction: column;
  flex-wrap: wrap; /* <-- allows multi-line flex container */
  width: 250px;
  height: 250px;
  background-color: silver;
}
flex-item {
  flex: 0 0 50px;
  width: 50px;
  margin: 5px;
  background-color: lightgreen;
}
<flex-container>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
</flex-container>

multi-line flex container (3 rows)

上面的图片有三个弹性线。

flex-container {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap; /* <-- allows multi-line flex container */
  width: 250px;
  height: 250px;
  background-color: silver;
}
flex-item {
  flex: 0 0 50px;
  height: 50px;
  margin: 5px;
  background-color: lightgreen;
}
<flex-container>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
</flex-container>


第三部分:关键词对齐属性

属性被分配给主轴和交叉轴(而不是X轴和Y轴)

虽然flex-direction属性控制flex项目的布局方向,但有四个属性控制对齐和定位。它们是:

  • align-items
  • align-self
  • align-content
  • justify-content

每个属性都永久分配给一个轴。

justify-content属性仅在主轴中起作用。

三个align-*属性仅在交叉轴中起作用。

常见的错误是认为这些属性固定于x轴和y轴。例如,justify-content总是水平的,而align-items总是垂直的。

但是,当flex-direction切换为column时,主轴变成了y轴,justify-content在垂直方向上起作用。

本文重点介绍交叉轴对齐。有关主轴对齐和 justify-content 属性的解释,请参见此文章:

定义

Flexbox规范提供了三个关键字属性用于交叉轴对齐:

  • align-items
  • align-self
  • align-content

align-items / align-self

align-items 属性沿着 flex 行的交叉轴对齐 flex 项。它适用于 flex 容器。

align-self 属性用于覆盖单个 flex 项上的 align-items。它适用于 flex 项。

以下是规范中的定义:

8.3. 交叉轴对齐:align-items 和 align-self 属性 弹性项目可以在当前行的交叉轴上对齐,类似于 justify-content,但是在垂直方向上。align-items 设置所有弹性容器项目的默认对齐方式。align-self 允许单独的弹性项目覆盖此默认对齐方式。 align-items / align-self 有六个可能的值:
- flex-start - flex-end - center - baseline - stretch - auto (仅适用于 align-self)
(点击上面的规范定义标题以获取每个值的描述。) < p > align-items 的初始值为 stretch,意味着 flex 项目将扩展容器交叉轴上的整个可用长度。

align-self 的初始值为 auto,这意味着它继承了 align-items 的值。

下面是在行方向容器中每个值的效果示例。

align-items align-self values

来源: W3C

align-content

这个属性比align-itemsalign-self稍微复杂一些。

以下是规范中的定义:

8.4. 对齐弹性线: align-content 属性

当在交叉轴上有额外空间时,align-content 属性将弹性容器内的弹性线对齐到弹性容器内,类似于 justify-content 将单个项目对齐到主轴上。请注意,此属性不会影响单行弹性容器。

align-itemsalign-self不同的是,它们移动弹性项目在其行内align-content则移动弹性线在容器内

有六个可能的值可用于 align-content

  • flex-start
  • flex-end
  • center
  • space-between
  • space-around
  • stretch

(有关每个值的描述,请单击上面的规范定义标题。)

以下是在行方向容器中每个值的效果示例。

enter image description here

来源:W3C

为什么align-content只在多行flex容器中起作用?

在单行flex容器中,行的交叉轴尺寸等于容器的交叉轴尺寸。这意味着行和容器之间没有空余空间。因此,align-content无法发挥作用。

以下是规范中相关的部分

只有多行flex容器才有交叉轴上的自由空间可以对齐行,因为在单行flex容器中,唯一的行会自动拉伸以填充空间。


第四部分:示例解释

enter image description here

在示例1中,align-selfflex-wrap: nowrap 配合使用,因为 flex 项目存在于单行容器中。因此,只有一条 flex 行,它与容器的高度相匹配。

flex-container {
  display: flex;
  flex-wrap: nowrap;
  align-content: flex-start;
  width: 250px;
  height: 250px;
  background-color: silver;
}
flex-item {
  flex: 0 0 50px;
  height: 50px;
  margin: 5px;
  background-color: lightgreen;
}
flex-item:last-child {
  align-self: flex-end;
  background-color: crimson;
}
<flex-container>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
</flex-container>

enter image description here

在示例2中,align-self失败,因为它存在于多行容器(flex-wrap:wrap)中,并且align-content设置为flex-start。这意味着flex行紧密地打包到交叉轴的起点,没有留下任何自由空间供align-self使用。

flex-container {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  width: 250px;
  height: 250px;
  background-color: silver;
}
flex-item {
  flex: 0 0 50px;
  height: 50px;
  margin: 5px;
  background-color: lightgreen;
}
flex-item:last-child {
  align-self: flex-end;
  background-color: crimson;
}
<flex-container>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
</flex-container>

enter image description here

例子 #3 的解释与例子 #1 相同。

flex-container {
  display: flex;
  flex-wrap: nowrap;
  align-items: flex-end;
  align-content: flex-start;
  width: 250px;
  height: 250px;
  background-color: silver;
}
flex-item {
  flex: 0 0 50px;
  height: 50px;
  margin: 5px;
  background-color: lightgreen;
}
<flex-container>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
</flex-container>

enter image description here

例子4的解释与例子2相同。

flex-container {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  align-content: flex-start;
  width: 250px;
  height: 250px;
  background-color: silver;
}

flex-item {
  flex: 0 0 50px;
  height: 50px;
  margin: 5px;
  background-color: lightgreen;
}
<flex-container>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
</flex-container>

enter image description here

示例#5是单行容器。因此,flex行的交叉尺寸等于容器的交叉尺寸,两者之间没有额外的空间。因此,align-content 在交叉轴上有额外空间时对齐flex行,但在此情况下无效。

flex-container {
  display: flex;
  flex-wrap: nowrap;
  align-content: center;
  width: 250px;
  height: 250px;
  background-color: silver;
}

flex-item {
  flex: 0 0 50px;
  height: 50px;
  margin: 5px;
  background-color: lightgreen;
}
<flex-container>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
</flex-container>

enter image description here

align-content的初始设置是stretch。这意味着如果没有指定其他值,容器将在伸缩行之间均匀分配可用空间。(当所有伸缩项都获得flex: 1时,在主轴上会产生类似的效果。)
此空间分配可能会导致行/列之间的宽间隙。较少的行会导致较宽的间隙。较多的行会导致较小的间隙,因为每个行会获得更小的空间份额。
要解决此问题,请从 align-content: stretch 切换到 align-content: flex-start。这会将行紧密地排在一起 (参见上面的示例#2和#4)。当然,这也会消除行中的任何自由空间,且 align-itemsalign-self 不再起作用。
这是一个相关的帖子:在伸缩项换行时去除它们之间的空格(间隙)

flex-container {
  display: flex;
  flex-wrap: wrap;
  /* align-content: center; */
  width: 250px;
  height: 250px;
  background-color: silver;
}

flex-item {
  flex: 0 0 50px;
  height: 50px;
  margin: 5px;
  background-color: lightgreen;
}
<flex-container>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
</flex-container>

enter image description here

如前面的例子所解释的那样,使用align-content: stretch,弹性行中可以有额外的空间,这使得align-itemsalign-self生效。任何其他align-content值都会打包行,消除额外的空间,使align-itemsalign-self无用。

flex-container {
  display: flex;
  flex-wrap: wrap;
  /* align-content: center; */
  width: 250px;
  height: 250px;
  background-color: silver;
}
flex-item {
  flex: 0 0 50px;
  height: 50px;
  margin: 5px;
  background-color: lightgreen;
}

flex-item:nth-child(even) {
  align-self: flex-end;
  background-color: crimson;
}
<flex-container>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
</flex-container>


3
align-content属性的解释:align-content属性修改了flex-wrap属性的行为。它类似于align-items,但不是用于对齐flex项目,而是用于对齐flex行。 - artamonovdev
2
在开始学习Flex并理解它后,最终发现Chrome在移动设备上的表现不同,我放弃了,现在我后悔了,如果我付出这么多努力,我本可以成为火箭科学家。哈哈 - Aadam
1
好的回答。然而,在轴图中添加第三个(z轴)并不是必要的,只会让事情变得更加复杂。 - Mihkel Mark
1
@MihkelMark,感谢您的反馈。我对答案进行了调整。希望现在更清晰了。 (顺便说一句,我选择了这张图片,因为它是我能找到的最好的没有版权限制的图片。) - Michael Benjamin
我无法在flex-wrap: wrap的容器中使justify-self适用于任何换行的内容。这只是MS Edge中的一个错误吗? - David Mays
@DavidMays,flexbox中没有justify-self属性。不起作用。https://dev59.com/tlwY5IYBdhLWcg3wgX6V - Michael Benjamin

6
首先,如果没有换行,align-content是无用的: w3c doc 引用: align-content属性在交叉轴上有额外空间时,将flex容器的行与flex容器对齐,类似于justify-content对齐主轴上的各个项。请注意,此属性对单行flex容器没有任何影响。
此外,在您的第二种情况中,align-self并没有失败。它是无用的,因为内部行已经被打包了。让我们创建一个空间,以便样式可以起作用:

flex-container {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  width: 250px;
  height: 250px;
  background-color: silver;
}
flex-item {
  flex: 0 0 50px;
  height: 50px;
  margin: 5px;
  background-color: lightgreen;
}
flex-item:last-child {
  align-self: flex-end;
  background-color: crimson;
}
flex-item:nth-child(5) {
  height: 90px;  /* added */
}
<flex-container>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
</flex-container>

同样地,在你的第四个例子中,如果你设置了可以看到它的条件,那么这个样式就会生效。

flex-container {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  align-content: flex-start;
  width: 250px;
  height: 250px;
  background-color: silver;
}

flex-item {
  flex: 0 0 50px;
  height: 50px;
  margin: 5px;
  background-color: lightgreen;
}
flex-item:nth-child(2),flex-item:nth-child(5) {
  height: 90px;  /* added */
}
<flex-container>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
  <flex-item></flex-item>
</flex-container>

最后,当您注释掉align-content时,它会恢复到stretch,这就是为什么行会增加大小以填充容器的原因。

stretch 行会拉伸以占用剩余空间。如果剩余的自由空间为负,则此值与flex-start相同。否则,自由空间平均分配给所有行,增加它们的交叉尺寸。


3
关于第一句话,可能更好的说法是“如果没有换行,就只有一行,这时align-content就没有用了”。我不太擅长用英语解释事情... - vals

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接