CSS 网格动画(方法 + 示例)

Avatar of Daniel Schwarz
Daniel Schwarz

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

我很高兴地告诉大家,CSS 的 grid-template-rowsgrid-template-columns 属性现在可以在所有主流网络浏览器中进行动画处理!好吧,CSS 网格从技术上讲已经很长时间支持动画了,因为它直接烘焙到 CSS 网格布局模块级别 1 规范中.

但是,对这些网格属性进行动画处理直到最近才在所有三个主要浏览器中获得支持。 我们来看看几个例子来激发创造力吧?

示例 1:扩展侧边栏

首先,这就是我们要讨论的内容

一个简单的两列网格。 现在,以前,你可能不会使用 CSS 网格来构建它,因为动画和过渡不受支持,但是如果你想要左侧列(可能是侧边栏导航)在悬停时扩展怎么办? 好吧,现在这是可能的。

我知道你在想什么:“给 CSS 属性做动画? 太简单了,我已经做了很多年了!” 我也是。 然而,在尝试特定用例时,我遇到了一些有趣的问题。

所以,我们想要转换网格本身(特别是grid-template-columns,它在示例中的.grid 类上设置)。 但是左侧列(.left)是需要:hover 伪类的选择器。 虽然 JavaScript 可以轻松地解决这个难题 - 谢谢,但不用谢 - 我们也可以用 CSS 独自完成。

让我们从 HTML 开始,逐步完成整个过程。 实际上非常标准……一个有两列的网格。

<div class="grid">
  <div class="left"></div>
  <div class="right"></div>
</div>

抛开装饰性的 CSS 不谈,首先你需要在父容器(.grid)上设置display: grid

.grid {
  display: grid;
}

接下来,我们可以使用grid-template-columns 属性定义和调整两列的大小。 我们将使左侧列超级窄,稍后在悬停时增加其宽度。 右侧列占据了剩余的所有空间,这得益于auto 关键字。

.grid {
  display: grid;
  grid-template-columns: 48px auto;
}

我们知道我们要给它做动画,所以让我们在添加它的时候也加一个transition,这样状态之间的变化就会变得平滑而明显。

.grid {
  display: grid;
  grid-template-columns: 48px auto;
  transition: 300ms; /* Change as needed */
}

这就是.grid 的全部! 剩下的就是应用悬停状态。 具体来说,我们将覆盖grid-template-columns 属性,以便左侧列在悬停时占据更多空间。

仅此一项并没有那么有趣,虽然 CSS 网格现在支持动画和过渡非常棒。 更有趣的是,我们可以使用相对较新的 :has() 伪类 来在子元素(.left)被悬停时对父容器(.grid)进行样式设置。

.grid:has(.left:hover) {
  /* Hover styles */
}

简单来说,这就是在说:“如果.grid 容器包含一个名为.left 的元素,并且该元素处于悬停状态,则对.grid 容器执行某些操作。” 这就是为什么:has() 通常被称为“父级”选择器。 我们终于可以根据子元素来选择父元素 - 不需要 JavaScript!

所以,让我们在悬停时将.left 列的宽度增加到30%.right 列将继续占据所有剩余的空间

.grid {
  display: grid;
  transition: 300ms;
  grid-template-columns: 48px auto;
}

.grid:has(.left:hover) {
  grid-template-columns: 30% auto;
}

我们也可以使用 CSS 变量,这取决于你的个人喜好,看起来可能更整洁(或者你可能在项目中使用 CSS 变量)。

.grid {
  display: grid;
  transition: 300ms;
  grid-template-columns: var(--left, 48px) auto;
}

.grid:has(.left:hover) {
  --left: 30%;
}

喜欢 CSS 网格现在可以做动画,但是我们只需要用九行 CSS 就能构建这个特定示例的事实更令人惊叹。

这是另一个由 Olivia Ng 提供的示例 - 相似的概念,但有内容(点击导航图标)

示例 2:扩展面板

此示例转换网格容器(列宽),同时也转换各个列(它们的背景色)。 它非常适合在悬停时提供更多内容。

值得记住的是,repeat() 函数有时会产生错误的过渡,这就是为什么我单独设置每列的宽度(即grid-template-columns: 1fr 1fr 1fr)。

示例 3:添加行和列

此示例以动画的方式“添加”一列到网格中。 然而 - 你猜对了 - 这种情况也有一个陷阱。 要求是“新”列不能隐藏(即设置为display: none),并且 CSS 网格必须在将宽度设置为0fr 时承认其存在。

因此,对于三列网格 - grid-template-columns: 1fr 1fr 0fr(是的,即使值为0 也必须声明单位!),会正确地过渡到grid-template-columns: 1fr 1fr 1fr,但是grid-template-columns: 1fr 1fr 不会。 事后看来,考虑到我们对 过渡如何工作 的了解,这实际上是完全合理的。

这是另一个由 Michelle Barker 提供的示例 - 相同的概念,但有一列额外的列和更多的活力。 确保以全屏模式运行它,因为它实际上是响应式的(没有技巧,只是良好的设计!)。

更多示例

为什么不呢?

这个“动画蒙德里安”是由 Chrome DevRel 为动画 CSS 网格提供的最初概念证明。 grid-rowgrid-column 使用span 关键字来创建你所看到的布局,然后使用 CSS 动画对grid-template-rowgrid-template-column 进行动画处理。 它看起来并不像那么复杂!

相同概念,但有更多 Michelle Barker 的活力。 可以做成一个不错的加载动画?

最后,带点怀旧(显示出我的年龄),由 Andrew Harvard 提供的不太像网格的动画 CSS 网格。 再一次 - 相同的概念 - 你只是看不到其他网格项目。 但别担心,它们就在那里。