我很高兴地告诉大家,CSS 的 grid-template-rows
和 grid-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-row
和 grid-column
使用span
关键字来创建你所看到的布局,然后使用 CSS 动画对grid-template-row
和grid-template-column
进行动画处理。 它看起来并不像那么复杂!
相同概念,但有更多 Michelle Barker 的活力。 可以做成一个不错的加载动画?
最后,带点怀旧(显示出我的年龄),由 Andrew Harvard 提供的不太像网格的动画 CSS 网格。 再一次 - 相同的概念 - 你只是看不到其他网格项目。 但别担心,它们就在那里。
不幸的是,浏览器仍然不支持以实际改变网格本身大小的方式对 grid-template-rows 进行动画处理。 网格内的内容会进行动画处理,但是如果你有一个网格,并且网格下方有内容,然后将其 grid-template-rows 动画处理成更小,那么网格下方的内容不会根据新的网格高度平滑地进行动画处理以改变其位置。
❤❤❤
很棒的文章,我喜欢将网格动画用于导航的想法。
这很棒! 谢谢分享
它似乎在 Firefox 中不起作用
很棒的网格动画,真的很感兴趣的文章。 将来肯定需要,谢谢。
它可以,除了使用 :has() 的示例,Firefox 还不支持。
我认为这是因为大多数示例都使用 :has() 选择器,而 Firefox 尚未支持。
我正在使用最新的 Firefox 开发者版,它可以正常工作
我也是。但我发现,:has() 选择器可以在 fireofx 的 about:config 中启用。即使这样,:has(.left:hover) 也不起作用,但 :has(.left) 可以起作用。
我真的很想用它 :(
哇,这太棒了。菜单动画和松饼饼干动画是这些动画中最棒的。最棒的是 codepen。
是的!我喜欢它!
Michelle Barker 的设计对我来说不起作用(最新版的 FF 在 Win11 for Workstations 上)。底部的行没问题,但上面的行文字只是在抖动,我并没有得到图片。
现在这真的非常棒,欢迎使用 Grid。
非常感谢你分享
现在我只使用 Strikingly 的拖放网站。它对我有用,但我知道这是所有更大更强大的网站正在做的事情。使用 CSS 编码,可以实现无限的彩色图形和模板。我希望能够在这一点上与大家保持联系,但现在我已经印象深刻了。
很棒的文章!我期待着尝试一下。只是想知道,动画网格属性的性能如何?在思考关于不要动画会导致布局或绘制的属性的建议。
目前没有问题。如果让我猜的话,这应该是处理这个概念/效果的最有效方式。
Firefox 109,有效。
注意:如果网格列设置为 min-content,动画将不起作用。
我在 FF 124.0.1 上,它有效,谢谢!
了解网格过渡是否为硬件加速会很有趣。似乎是这样,但我并不确定。
不是。每个动画帧都会导致样式重新计算、布局和绘制
但样式重新计算部分在我的电脑上运行得很快。我会进一步调查它。