使用 CSS 网格定位叠加内容

Avatar of Ryan Mulligan
Ryan Mulligan

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 $200 免费信用额度!

这对 2021 年的任何 Web 开发人员来说都不算新闻:CSS Grid 是一个功能强大的工具,用于创建复杂、独特的二维现代 Web 布局。

最近,我一直在尝试使用 CSS 网格和对齐属性来创建包含多个重叠元素的组件布局。这些布局可以使用绝对定位以及各种偏移值 (toprightbottomleft)、负边距和变换进行样式化。但是,使用 CSS 网格,可以利用更逻辑、更易读的属性和值来定位叠加元素。以下是一些这些网格属性发挥作用的示例。

如果您还不熟悉 grid-template-areasgrid-area 属性,建议您阅读一下。

在有限尺寸内扩展图像

在演示中,有一个复选框可以切换溢出可见性,以便我们可以看到图像尺寸在较大的视窗宽度下扩展到容器之外的位置。

这是一个常见的英雄部分,标题与图像重叠。尽管图像被限制了 max-width,但在桌面设备上它会扩展得相当高。因此,内容策略团队要求尽可能多地将英雄部分下方的相关页面内容保持在视窗中可见。结合这种布局技术和使用 CSS clamp() 函数的流体容器 max-height,我们可以开发出根据可用视窗空间调整大小的内容,同时将英雄图像锚定到容器的中心。

CSS clamp() 以及 min()max() 比较函数在所有现代浏览器中都得到了很好的支持。您还没有使用过吗?Ahmad Shadeed 在 这篇文章 中进行了一次精彩的深入探讨。

打开此 Pen 并调整视窗宽度。根据图像尺寸,容器高度会扩展,直到达到最大高度。请注意,图像会继续增长,同时保持在容器的中心。调整视窗高度,容器将在 clamp() 函数中定义的 max-height 的下限和上限值之间灵活变化。

在使用网格进行布局样式之前,我可能会尝试对图像和标题进行绝对定位,使用 纵横比填充技巧 创建响应式高度,并使用 object-fit 保持图像的比例。类似这样的代码可能会实现这个效果

.container {
  position: relative;
  max-height: clamp(400px, 50vh, 600px);
}

.container::before {
  content: '';
  display: block;
  padding-top: 52.25%;
}

.container > * {
  max-width: 1000px;
}

.container .image {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.container .title {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 100%;
  text-align: center;
}

也许可以将代码缩减更多,但仍然需要大量的样式。使用 CSS 网格管理相同的响应式布局将简化这些布局样式规则,同时使代码更易读。请查看以下迭代

.container {
  display: grid;
  grid-template: "container";
  place-items: center;
  place-content: center;
  overflow: hidden;
  max-height: clamp(450px, 50vh, 600px);
}

.container > * {
  grid-area: container;
  max-width: 1000px;
}

place-content: center 指示图像从容器的中间继续向外增长。删除此行,您会发现,尽管图像仍通过 place-items 垂直居中,但在达到 max-height 后,图像会粘贴到容器块的顶部,并继续超出其底部的比例。设置 place-content: end center,您会看到图像溢出容器的顶部。

这种行为在概念上可能与在图像上应用 object-fit: cover 作为 样式方法 非常相似,它可以保持图像的内在比例,同时调整大小以填充其内容框尺寸(它被用在绝对定位迭代中)。但是,在这个网格上下文中,图像元素控制其父元素的高度,一旦父元素的 max-height 被达到,图像就会继续扩展,保持其比例,并且如果父元素的溢出可见,则图像将完全可见。object-fit 甚至可以与 aspect-ratio 属性一起使用,在这里创建英雄图像的 纵横比 模式。

.container .image {
  width: 100%;
  height: auto;
  object-fit: cover;
  aspect-ratio: 16 / 9;
}

叠加 grid-area

接下来,对于容器的直接子元素,grid-area 将它们排列在一起,以便它们重叠相同的空间。在这个示例中,使用命名的网格区域的 grid-template-areas 使代码更易读,并且在组件库中作为其他叠加样式布局的模式效果很好。也就是说,可以删除模板规则并使用整数,而不是 grid-area: container 来获得相同的结果

.container > * {
  grid-area: 1 / 1;
}

这是 grid-row-startgrid-column-startgrid-row-endgrid-column-end 的简写。由于此演示中的兄弟元素都共享相同的单行/单列区域,因此只需设置开始行即可获得所需结果。

设置 place-self 以进行自我定位

另一种常见的叠加模式可以在图像轮播中看到。交互式元素通常放置在轮播视窗的顶部。我已经扩展了第一个演示,并用轮播替换了静态英雄图像。

与之前一样:这种布局可以使用绝对定位,并在一些属性中使用整数值来推动和拉动元素围绕其父容器。相反,我们将重新使用上一个演示中的网格布局规则集。应用后,它将按预期显示:所有子元素都集中在容器内,彼此重叠。

Picture of a cute light brown puppy with the words Welcome to the Snuggle Zone on top in white. The text overlaps over text elements and is hard to read.
通过在容器上声明 place-items: center,所有其直接子元素将彼此重叠。

下一步是在单个元素上设置对齐值。place-self 属性(align-selfjustify-self 的简写)提供对单个项目在容器内位置的精细控制。以下是所有布局样式

.container {
  display: grid;
  grid-template:"container";
  place-items: center;
  place-content: center;
  overflow: hidden;
  max-height: clamp(450px, 50vh, 600px);
}

.container > * {
  grid-area: container;
  max-width: 1000px;
}

.title {
  place-self: start center;
}

.carousel-control.prev {
  place-self: center left;
}

.carousel-control.next {
  place-self: center right;
}

.carousel-dots {
  place-self: end center;
}

只有一个问题:当图像超过容器尺寸时,标题和轮播点指示器会被拉到溢出区域之外。

为了将这些元素正确地包含在父元素中,grid-template-row 值需要为容器的 100%,这里设置为一个分数单位。

.container {
  grid-template-areas: "container";
  grid-template-rows: 1fr;
}

对于此演示,我依赖于 grid-template 简写(我们将在本文后面再次看到它)。

.container {
  grid-template: "container" 1fr;
}

在提供了这些小更新后,即使轮播图像超出轮播的边界,叠加元素也会留在父容器中。

对齐和命名的 grid-template-areas

让我们再举一个示例,使用上一个叠加布局方法。在此演示中,每个框都包含定位在图像顶部的不同区域中的元素。

对于第一次迭代,声明一个命名的模板区域以将子元素叠加在父元素空间上,类似于之前的演示

.box {
  display: grid;
  grid-template-areas: "box";
}

.box > *,
.box::before {
  grid-area: box;
}

图像和半透明叠加现在覆盖了框区域,但是这些样式规则也会将其他项目拉伸到整个空间。看来现在是时候使用 place-self 为这些元素添加一些对齐魔法了!

.tag {
  place-self: start;
}

.title {
  place-self: center;
}

.tagline {
  place-self: end start;
}

.actions {
  place-self: end;
}

看起来很棒!每个元素都如预期定位在其定义的位置上,覆盖在图像上。好吧,几乎。底部区域有一些细微差别,在那里标语和操作按钮位于。将鼠标悬停在图像上以显示标语。这在台式机屏幕上使用短字符串可能没问题,但是如果标语变得更长(或视窗中的框变得更小),它最终会扩展到操作按钮的后面。

A two by two grid of images with text overlaid on top, as well as a tag label in the top right corner and, tagline in the bottom left corner and actions to like and share in the bottom right corner of each one.
请注意,第二行第一个框中的标语如何与操作按钮重叠。

为了清理这种情况,grid-template-areas 为标语和操作使用命名区域。引入 grid-template-columns 规则,以便操作容器仅扩展以适应其按钮的大小,而标语使用 1fr 值填充剩余的内联区域。

.box {
  display: grid;
  grid-template-areas: "tagline actions";
  grid-template-columns: 1fr auto;
}

这也可以与 grid-template 简写结合使用。列值在斜杠之后定义,如下所示

.box {
  grid-template: "tagline actions" / 1fr auto;
}

现在,由于“box”关键字已被删除,grid-area 被转换为整数。

.box > *,
.box::before {
  grid-area: 1 / 1 / -1 / -1;
}

一切都应该看起来与之前一样。现在进行最后的润色。taglineactions 关键字设置为其相应的元素 grid-area

.tagline {
  grid-area: tagline;
  place-self: end start;
}

.actions {
  grid-area: actions;
  place-self: end;
}

现在,当将鼠标悬停在演示中的卡片上时,当文本变得太长时,标语会换行,而不是像之前那样推过操作按钮。

命名网格线

回顾一下这段代码的第一个版本,我真的很喜欢将默认的 grid-area 设置为 box 关键字。有一种方法可以将其恢复。

我将在模板中添加一些命名网格线。在下面的 grid-template 规则中,第一行定义了命名的模板区域,它也表示行。斜杠之后是显式的列大小(为了可读性,移到新行)。[box-start][box-end] 自定义标识符代表 box 区域。

.box {
  display: grid;
  grid-template: 
    [box-start] "tagline actions" [box-end] /
    [box-start] 1fr auto [box-end];
}

.box > *,
.box::before {
  grid-area: box;
}

在方括号中使用 -start-end 语法传递名称,可以定义该名称的区域。这个名称被称为 自定义标识符,可以是任何内容,但 应避免使用 CSS 规范中的词语

逻辑放置值

在最后一个示例中,最有意思的部分之一是使用逻辑值(例如 startend)来放置元素。如果 directionwriting-mode 发生改变,则元素会相应地重新定位。

当从下拉菜单中选择“从右到左”方向时,内联开始和结束位置将被反转。这种布局可以适应从右到左阅读的语言(如阿拉伯语或希伯来语),而无需覆盖任何现有的 CSS。

总结

我希望您喜欢这些演示,并且它们能为您的项目布局提供一些新的想法——我在 CodePen 上整理了一个 示例集,您可以查看。CSS Grid 规范中包含的强大功能令人难以置信。花点时间回想一下使用 浮动clearfix 来进行基本网格行设计的那些日子,然后再回到现在,看看今天的 CSS 辉煌的布局和显示属性。要使这些东西正常工作并非易事,所以让我们为 CSS 工作组的成员鼓掌。网络空间在不断发展,他们也一直在努力让它成为一个有趣的构建场所。

现在让我们发布 容器查询,真正让这场派对开始。