笔记:flex-wrap 和 align-self,align-items,align-content 之间的关系

5,330 阅读10分钟
原文链接: moxo.io

问题

在使用 flexbox 布局中,flex-wrapalign-selfalign-itemsalign-content 之间到底是什么关系?在日常切页面的时候,会发现在容器上使用 flex-wrap 的不同属性对子项会造成不同的影响,请看下面七个例子,施加的效果各不相同。问题就是对这些属性如何相互作用的困惑。

七个例子

原问中题主一共举了七个例子,因为篇幅关系,第一部分中只用文字列出,具体的可以参考每个例子最后列出的 CodePen 链接。另,所有例子在文末第四部分,针对问题中不同例子的解答中都一一对应列出并做了解释。

  1. 容器 flex-wrap: nowrap,子级 align-self: flex-end 有效,CodePen
  2. 容器 flex-wrap: wrap,子级 align-self: flex-end 无效,CodePen
  3. 容器 flex-wrap: nowrap + align-items: flex-end 对子级有效,CodePen
  4. 容器 flex-wrap: wrap +align-items: flex-end 对子级无效,CodePen
  5. 容器 flex-wrap: nowrap + align-content: center 对子级无效,CodePen
  6. 容器 flex-wrap: wrap,没有 align-itemsalign-content,子级之间会有空隙,CodePen
  7. 容器 flex-wrap: wrap,同时去除掉 align-content,子级 align-self: flex-end 又可以生效,CodePen

七个例子中:1、2 对比外层容器包含许多子项,flex-wrap 属性值对子项的 align-self 属性的影响,3、4 是外层容器包含许多子项, flex-wrap 属性值对容器自身的 align-items 的影响,5、6、7 与之前四个不同,这三个之间没有对比关系。

解答

flex-wrap 属性的使用效果是控制其内部的元素是否可以换行,虽然就这点来说这是一个非常基础的属性,但其实它对整个 flexbox 布局产生的其他方面的影响也很大。

首先,flex-wrap 属性会会定义你使用 flexbox 容器的类型:

  1. flex-wrap: nowrap 定义下的 flex 容器是一个 single-line 的 flex 容器;
  2. flex-wrap:wrap | wrap-reverse 会定义一个 multi-line 的 flex 容器。

其次,align-itemsalign-self 两个属性在 single-linemulti-line 两种 flex 容器中都可以工作。然而,但只有在坐标轴还有剩余空间的时候才会产生效果。

最后,align-content 属性只能 multi-line 的 flex 容器中产生效果,它会直接无视被定义为 single-line 的 flex 容器。

原理

flexbox 规范文档对具体 flex item 的对齐给出了四个可能的属性关键词:

想要彻底理解这四个属性是如何工作,对具体施加到的元素产生什么样的效果,首先得先理解 flexbox 布局中的结构具体是怎么样的。

第一部分,理解 flexbox 中的 x 轴和 y 轴

x 轴和 y 轴

flexbox 容器中的 x 轴(水平方向)和 y 轴(垂直方向)

flexbox 容器中的内容只在 x 轴(水平方向)和 y 轴(垂直方向)上可以工作。其中的子级元素,就是被我们称为 flex item 的那些,可以以这两条轴的方向做对齐操作。而这是 flexbox 容器中对齐效果最最基本的部分。

主轴(main axis)和交叉轴(cross axis)

flexbox 布局中,覆盖在 x 和 y 轴之上的,被称为主轴和交叉轴

在 flex 布局中,覆盖在 x 和 y 轴之上的,被称为主轴和交叉轴。 默认情况下,主轴在水平方向上,而交叉轴在垂直方向上,同时这也是 flexbox 规范文档为 flexbox 规定的初始值(这里说到的初始值指的是 flex-direction: row,而 align-content: strecth。)。

但和 x 和 y 轴不同的是,主轴和交叉轴两条轴线可以变换位置。

flex-direction 属性

The flex-direction property specifies how flex items are placed in the flex container, by setting the direction of the flex container’s main axis. This determines the direction in which flex items are laid out.

5.1. Flex Flow Direction: the flex-direction property

上图中,主轴在水平方向上同时交叉轴在垂直方向。就像之前提到的,这是一个 flexbox 容器的初始值。但主轴和交叉轴两条轴线的方向可以通过 flex-direction 属性的变化而被改变。这个属性的值控制着主轴的方向,由他来决定 flex item 是从水平方向对齐、还是以垂直方向对齐。

也需要明确的一点是:交叉轴和主轴始终是相互垂直的

第二部分

  • 在 flex 容器中,item 以线来显示,被认为是「flex line」;
  • 这个「flex line」依据 flex-direction 值的变化,有时候是 row,而有时候是 column
  • flex 容器中可以有一条或更多的「flex line」,至于到底有多少条?这就取决于 flex-wrap 这个值。

single-line flex 容器

首先,flex-wrap: nowrap 建立的就是 single-line flex 容器,其中所有的子级 flex item 的位置都被强制安排在一条线上(即使超出了容器,overflow)。

CodePen 链接

一个 single-line 的 flex 容器

multi-line flex 容器

flex-wrap: wrap 或者 wrap-reverse 会产生一个 multi-line 的容器,在其中,flex 容器可以产生很多线(line)。

CodePen 链接

一个 multi-line 的 flex 容器,配合 flex-direction: column,以垂直方向呈现。

CodePen 链接

一个 multi-line 的 flex 容器,配合 flex-direction: row,以水平方向呈现。

第三部分,对齐属性

对齐的属性是被是加到了主轴和交叉轴,而不是(x 和 y)。

flex-direction 属性控制整个 flex 布局中 item 如何定位时,有四个属性可以用来规定 item 的对齐(alignment)和定位(positioning)方式:

这四个属性中的任何一个施加得到的效果会永久的产生在一条轴(axis)上。

  • justify-content 只会对主轴(main axis)产生影响。
  • 剩下的三个 align-* 只会在 cross 上产生影响。

有一个常见的错误,开发者会觉得这些属性只会在 x 和 y 轴上产生作用。比如说,justify-content 只在水平方向上产生作用,而 align-items 只能在垂直方向爱你关上产生影响。然而,当 flex-direction 被设置为 column 的时候,主轴(main axis)就会使 y 轴,这个时候 justify-content 就会在交叉轴、也就是垂直方向上产生效果。

这篇文章将会围绕交叉轴的对齐方式,如果对主轴的对齐和 justify-content 属性的工作方式感兴趣,可以参考这篇:In CSS Flexbox, why are there no “justify-items” and “justify-self” properties?

定义:

flexbox 布局规范对在 cross-axis 的对齐方式提供了三个关键词属性:

align-items/align-self

  • align-item 属性需要施加在 flex 容器上,它规定的是 flex 容器中 item 在交叉轴中的对齐方式;
  • align-self 属性则施加在 flex 容器中的 item 上,覆盖了外部容器规定的 align-items 属性,同样也只规定在交叉轴上的对齐方式。

Flex item can be aligned in the cross axis of the current line of the flex container, similar to justify-content but in the perpendicular direction. align-items sets the default alignment for all of the flex container’s items. align-self allows this default alignment to be overridden for individual flex items.

8.3. Cross-axis Alignment: the align-items and align-self properties

align-items/align-self 有六个值:

align-items 的初始值是 stretch,意味着容器中所有的 flex item 会拉升撑满 cross axis 的长度。 align-self 的初始值是 auto,意味着直接继承容器的 align-item 的初始值。

这张图示范了在设置 flex-direction: row 的情况下,以上六个值产生的效果。

这张图示范了在设置 flex-direction: row 的情况下,以上六个值产生的效果。

align-content

The align-content property aligns a flex container’s lines within the flex container when there is extra space in the cross-axis, similar to how justify-content aligns individual items within the main-axis. Note, this property has no effect on a single-line flex container.

8.4. Packing Flex Lines: the align-content property

align-content 属性的作用会比 align-itemsalign-self 更加复杂一些。对比 align-itemsalign-self 直接移动 item 自身在交叉轴上的基线,align-content 移动的是容器自身的 flex line

align-content 的六个值:

这张图示范了在设置 flex-direction: row 的情况下,以上六个值产生的效果。

这张图示范了在设置 flex-direction: row 的情况下,以上六个值产生的效果。

为什么 align-content 只在 multi-line 的 flex 容器中才有效果?

Only multi-line flex containers ever have free space in the cross-axis for lines to be aligned in, because in a single-line flex container the sole line automatically stretches to fill the space.

Packing Flex Lines: the align-content property

在 single-line 的 flex 容器中,侧轴线上的空间(cross size of the line)等同于容器的交叉轴上的空间,意味着侧轴这条线和容器之间没有多余的空间,导致 align-content 没有发挥任何作用的余地(原文: In a single-line flex container, the cross size of the line is equal to the cross size of the container. This means there is no free space between the line and the container. As a result, align-content can have no effect. )。

第四部分,针对问题中不同例子的解答

例子一

一个 multi-line 的 flex 容器,配合 flex-direction: row,以水平方向呈现。

容器的 flex-wrap 设为 nowrap,按照上文所指出的这意味着该容器是一个 single-line flex 容器,single-line 的 flex 容器下 align-content 属性无法工作,但是 align-items/align-self 可以起到该有的效果。

CodePen 链接

例子二

这里对比例子一,并不是这里最后一个 item 的 align-self: flex-end 没有了效果,而是同样有效果,只是效果本身的显现被容器的 align-content: flex-start 影响了。

容器设置了 flex-wrap: wrap,这就成了一个 multi-line 的 flex 容器,设置了 align-content: flex-start,上面说到,align-content 属性的效果需要在 multi-content 的容器中才会显现,这里就是将两行 item 元素挤压到了容器的顶部(flex-start)。同时,最后一个 item 设置了 align-self: flex-end

CodePen 链接

例子三

解答参考例一。

容器设置 flex-wrap: nowrap 使容器自身成为了一个 single-line 的 flex 容器,配合 align-items: flex-end,所有顶部的空间都会被释放,item 会在容器的底部排列。

CodePen 链接

例子四

解答参考例二。

容器设置了 flex-wrap: wrap,因而容器就成了一个 multi-line 的 flex 容器,align-content: flext-start 随之起了作用,按图上的红色虚线勾出的方式将两行 item 挤压到了容器的顶部,然后一次排列。

CodePen 链接

例子五

容器设置 flex-wrap: nowrap,因而容器就成了一个 single-line 的 flex 容器。虽然只是一条线,但这一条线(flex line)的高度不是像显示的那样只在 flex 容器的顶部,而要想象这条线的高度已经撑满了整个容器,作为结果,容器中就没有多余的空间来挤压释放给 align-contents: center 属性。不过,如果在这里设置 align-items 或者在 item 上设置 align-self 依然有效。

CodePen 链接

例子六

容器设置 flex-wrap: wrap,因而容器就成了一个 multi-line 的 flex 容器,而且设置了 align-content: stretch,前提是现在的 item 一行放不下,被分配到了两行来显示,stretchalign-content 意味容器的基线(line)有两行,容器的空间被平均分配到了两行上。

这里出现的空隙是由于,每一条基线(line)的高度减去 item 本身的高度多余出来的部分。也就是说,如果 item 的数量多,容器中的线(line)多,那么空隙就会显得少一些,反之则会显得多一些。

CodePen 链接

例子七

像例六中说的,align-content: stretch 平均分配容器的空间给容器中的线(line,其实就是平均分配给产生的每一行),每一行都会有多出来的空间(参考例六中的说明),这里多出来的空间就让 align-items: flex-end 发挥作用。如果在这里将 align-content 换成任何别的值,比如 flex-start,就会把现在这些多余的空间释放,align-itemsalign-self 就不会起到任何作用。

CodePen 链接