[译] Flexbox 对齐全知道!

2,145 阅读12分钟

原文链接:Everything You Need To Know About Alignment In Flexbox, by Rachel Andrew

本系列的第一篇文章 中,我解释了当在元素上声明 display: flex 的时候,发生了什么。这一次我将带领大家浏览一下对齐属性(alignment properties),看看它们是如何在 Flexbox 中起作用的。如果你曾困惑不知道该使用 align-* 属性还是 justify-* 属性的话,我希望本文能够帮你解决这个困惑。

Flexbox 对齐属性简史

在整个 CSS 布局历史中,如何能自如地在两个轴上对齐元素可能是 Web 设计领域的最让人头大的问题了。因此第一次看见 Flexbox 中对齐项目的功能时,是非常令人兴奋的。比如,下面两行 CSS 便能轻松做到居中对齐:

See the Pen Smashing Flexbox Series 2: center an item by Rachel Andrew

你可能认为这些属性仅是作为 Flexbox 布局中的对齐属性使用的,事实上不是,这些属性现在都定义在了 Box Alignment 规范 中。这个规范描述了这些对齐属性是如何在不同的布局上下文(layout contexts)中起作用的。也就是说,我们可以在 CSS 网格(Grid)中使用在弹性布局中用到的这些对齐属性——当然,未来可能还会有其他的布局上下文出现。因此,未来 Flexbox 中新的对齐功能将会在 Box Alignment 规范中定义,而不是在 Flexbox 规范中。

属性

许多人跟我说在使用 Flexbox 过程中,很容易被“这里该使用 align- 属性还是 justify 属性?”的问题搞懵圈。这里告诉你一个简单的记忆方法:

  • justify- 操作的是主轴(main axis)对齐,对齐方向与 flex-direction 方向一致。
  • align- 操作的是交叉轴(cross axis)对齐,对齐方向与 flex-direction 方向垂直。

这里我们使用的是“主轴”和“交叉轴”,而没有说“水平方向”或“垂直方向”,是因为前者(两根轴)的方向跟物理方向并不是关联的。

主轴对齐:justify-content

我们从主轴对齐开始讲起。在主轴上对齐,我们使用的是 justify-content 属性。这个属性把所有的 Flex 项目当成一组,控制项目之间的空间分配。

justify-content 的初始值是 flex-start。这就是为什么,在声明 display: flex 之后,所有的 Flex 项目都是在弹性线(flex line) 的起始处对齐的。当 flex-directionrow 且处于从左到右的语言环境的时候(比如英语),这个起始处就位于左边。

Flex 项目在起始处对齐

需要注意的是,justify-content 仅在有剩余空间可供分配的情况下才起作用。如果在主轴上,Flex 项目分配完毕后,已无可供分配的剩余空间,justify-content 不会起任何作用。

无空间可供分配

如果我们给 justify-content 使用的是 flex-end,那么所有项目都是在弹性线末尾对齐,剩余空间位于头部。

Flex 项目在末尾处对齐

剩余空间的分配还有更多方式。比如,我们可以空间在 Flex 项目之间平均分配,需要用到 justify-content: space-between。这种情况下,第一个和最后一个 Flex 项目会靠在容器边缘,然后剩余空间会在项目之间均等分配。

剩余空间在项目之间平均分配

也可以使用 ustify-content: space-around。这种情况下,剩余空间会在每个项目的两边分配。

剩余空间在每个项目的两边分配

译注:因为每个项目两边都分到了一样的空间。因此,项目间的间距会是项目与容器边缘间距的两倍。图中的容器设置了 padding 值,因此看出的效果可能没那么明显,在此告知大家。

Box Alignment 规范还为 justify-content 属性定义了一个新值 space-evenly ,它并未出现在 Flexbox 规范中。使用了这个值后,项目与容器之间、项目与项目之间的分配的空间大小是一样的。

项目均匀分布于容器内

下面的 demo 展示了在不同取值情况下的项目分配:

See the Pen Smashing Flexbox Series 2: justify-content with flex-direction: row by Rachel Andrew

flex-direction 取值 column 的时候,这些值的作用效果是一样的。当然了,除非你给 Flex 容器一个高度或者容器本身是 block-size 的,否则是没有额外空间可供非配的。请看下面的 demo:

See the Pen Smashing Flexbox Series 2: justify-content with flex-direction: column by Rachel Andrew

交叉轴对齐:align-content

如果 Flex 容器使用了 flex-wrap: wrap,我们就能得到多行(multiple flex lines) 显示的 Flex 项目,align-content 就是处理在交叉轴上行之间的剩余空间分配。当然,前提是在交叉轴上存在剩余空间可供分配。在下面的例子中,交叉轴以列的形式在块方向(block direction)上布局,我把容器的高度设置为 60vh 了,这比 Flex 项目所需要的空间要多,这样我们就有空间在垂直方向上进行分配了。

我们可以使用下图里列举的值,布局交叉轴上的对齐:

See the Pen Smashing Flexbox Series 2: align-content with flex-direction: row by Rachel Andrew

如果 flex-direction 取值 column,则 align-content 将按如下方式布局:

See the Pen Smashing Flexbox Series 2: align-content with flex-direction: column by Rachel Andrew

类似于 justify-contentalign-content 是把多行看成一个组来分配剩余空间的。

简写属性:place-content

阅读 Box Alignment 规范,发现有一个简写属性 place-content,这个属性是 justify-content 属性和 align-content 属性的简写形式。place-content 接收两个值,第一个值指定 align-content,第二个值指定 justify-content;如果只指定一个值的话,两个属性都设置为这个值。因此:

.container {
    place-content: space-between stretch;
}
/* 等效于 */
.container {
    align-content: space-between;
    justify-content: stretch;
}

/* 如果是这样使用的 */

.container {
    place-content: space-between;
}
/* 则等效于 */
.container {
    align-content: space-between;
    justify-content: space-between;
}

交叉轴对齐:align-items

我们已经知道,可以将一组 Flex 项目或将多行弹性线(flex lines)作为组进行对齐。但是还有一个小小的需求,就是希望在交叉轴上以彼此关联的方式对 Flex 项目做对齐。Flex 容器会有一个高度,这个高度可能是由最高的那个 Flex 项目决定的。

容器的高度是由第三个项目的高度决定的

当然,也可以直接给 Flex 容器一个高度:

直接在容器上定义了高度

其他 Flex 项目之所以会伸展与最高项一样的高度,是因为 align-items 属性的初始值是 stretch 。项目在交叉轴上伸展,直到尺寸和 Flex 容器的尺寸一样。

我们要注意到 align-items 的作用点是哪里。对于多行 Flex 容器,每一行都像是一个新的 Flex 容器,那一行里最高的 Flex 项目将决定改行其他项目的高度。

除了初始值 stretch,还可以使用 flex-start,此属性会让 Flex 项目沿着容器的起点处对齐,不会伸展到与容器一样的高度了。

Flex 项目沿着交叉轴的起点处对齐

flex-end 则让 Flex 项目沿着交叉轴的终点处对齐。

Flex 项目沿着交叉轴的终点处对齐

如果使用的是 center,则所有项目的沿着中心对齐。

在交叉轴上居中对齐项目

还可以基于基线对齐。与上面的基于项目中心线对齐不同,这是根据文本的基线对齐的:

基线对齐

可以查看下图,发现不同值产生的效果:

See the Pen Smashing Flexbox Series 2: align-items by Rachel Andrew

单个项目对齐:align-self

align-items 的意思是一次设置所有 Flex 项目的对齐方式。实际是把所有 Flex 项目的 align-self 属性做了统一的设置。你也可以在单个 Flex 项目上使用 align-self 属性,指定在行(flex line)内与其它 Flex 项目不同的对齐方式。

在下例中,我们在 Flex 容器上将 align-items 属性设置为 center,并且为第一个和最后一个项目分别指定了单独的对齐方式。

See the Pen Smashing Flexbox Series 2: align-self by Rachel Andrew

为什么没有 justify-self?

译注:

原文作者这里的解释有点牵强。详细可参考 stackoverflow 上的回答:In CSS Flexbox, why are there no “justify-items” and “justify-self” properties?

总结下来的话,就是没有必要,因为使用下面即将要说的 auto margin 方案就能解决。规范中也是介绍使用 auto margin 解决主轴上部分项目的个性化对齐问题的。

一个常见的问题是,如何在主轴对齐一个项目或者一组项目。为什么 Flexbox 主轴上没有 -self 属性?如果你知道 justify-contentalign-content 是用来分配剩余空间的,那么可能就能搞懂为什么没有 self-alignment 属性了。我们把 Flex 项目看成一组,并对可用空间进行分配——在组的头部或者尾部或者再项目之间。

我们可以思考下 justify-contentalign-content 属性在 CSS Grid 布局中的作用。这两个属性在 Grid 中是用来分配*网格轨迹之间(between grid tracks)*的间距的。它们把轨迹看成是一个组,这些属性就是用来分配这些轨迹之间的间距的。由于我们同时在 Grid 和 Flexbox 中对一个组进行操作,因此我们不能单独针对一个项目来做一些不同的事情。然而,有一种方法可以实现当您在主轴上请求 self 属性时所要求的那种布局,那就是使用 auto margin。

在主轴上使用 auto margin

如果你有过用 CSS 居中显示一个块元素的经验(通过给内容页设置左右 marginauto),你就已经理解 auto margin 实现居中的行为。如果我们设置了一个方向上的 margin: auto,那么这个 margin 会尽可能的占据该方向的空间大小。在使用 margin 居中块元素的例子中,我们将左右 margin 都设置为了 auto,它们每个都尽可能的争夺空间,最终将块元素推倒了中间的位置。

auto margin 在主轴对齐上同样表现优秀,它可以实现单个项目或者一组项目的对齐。下例中,我实现了一个常见的设计模式——我使用 Flexbox 实现了一个导航栏,项目显示为一行,使用了初始值 justify-content: start。我想让最后一个项目显示在弹性线(flex line)的末尾(当然,前提假设是这一行上有足够的空间)。

我选中了这个项目,并且给了它 margin-left: auto。这就是说项目的左边会占据尽可能多的空间,也就是说这个项目会被推倒了右边。

See the Pen Smashing Flexbox Series 2: alignment with auto margins by Rachel Andrew

如果在主轴上使用了 auto margin,justify-content 就会失去效果,因为 auto margin 会占据所有本来要给 justify-content 分配的空间。

回退对齐

每个对齐方法都有一个详细的回退对齐,这是在请求的对齐不能实现的情况下发生的事。例如,如果在容器中只有一个项目,并且使用了 justify-content: space-between,那么结果如何?答案是会使用回退对齐 flex-start,然后发现这个项目在 Flex 容器的起点处对齐了;对 justify-conrtent: space-around 的情况,回退对齐是 center

当前规范中,我们还无法修改回退对齐,如果你想让 space-between 的回退方案采用 center 而非 flex-start 的话,是做不到的。 规范中有一个 Note 提到未来的版本中可能会支持。

Safe 和 Unsafe 对齐

Box Alignment 规范最近新增了 Safe 和 Unsafe 对齐的概念,对应关键字 safeunsafe

下面的代码中,最后一项对于 Flex 容器来说太宽了,且对齐方式不安全。Flex 容器位于页面左侧,当溢出超出页面边界时,发现这一项被切断了。

.container {  
    display: flex;
    flex-direction: column;
    width: 100px;
    align-items: unsafe center;
}

.item:last-child {
    width: 200px;
}

不安全的对齐方式导致的对齐结果可能导致数据丢失

而安全对齐方式会避免数据丢失的出现,将溢出转移到另一边。

.container {
    display: flex;
    flex-direction: column;
    width: 100px;
    align-items: safe center;
}

.item:last-child {
    width: 200px;
}

安全对齐避免数据丢失

See the Pen Smashing Flexbox Series 2: safe or unsafe alignment by Rachel Andrew

总结

对齐属性起源于 Flexbox,但是现在位于独立的规范中,并可用于其他布局上下文中。以下几个关键事实将帮助你记住如何在 Flexbox 中使用它们:

  • justify- 主轴对齐,align- 交叉轴对齐;
  • 使用 align-contentjustify-content 属性时需要有额外的剩余空间;
  • align-contentjustify-content 属性把 Flex 项目看成一组来处理。因此对这些属性而言,没有针对单个项目的 -self 对齐属性。
  • 如果你希望在主轴上对齐一个项目,或者把部分项目作为一组对齐的话,可以使用 auto margin。
  • align-items 用于统一设置 Flex 项目上的 align-self 属性。在 Flex 项目上使用 align-self 用于单独设置此项目的对齐方式。

(完)