
CSS 中的滚动进度动画
与滚动相关的动画通常可以为网站增添一抹优雅,但长期以来一直是 JavaScript 的专属领域。现在,一项全新的规范正在实施,使我们能够使用 CSS 创建丰富的滚动驱动体验!
当我们想到滚动驱动的动画时,通常会想到两种情况之一
- 当用户滚动时发生的动画,动画的进度与滚动进度明确关联。例如,一篇长文章的进度条。
- 当元素进入、退出或穿过可见区域时,该元素上发生的动画——通常是视窗,但它也可以是另一个可滚动容器的可见部分(这被定义为滚动端口)。
滚动驱动动画规范涵盖了这两种类型的动画。在本文中,我们将首先看一下滚动进度时间线,顾名思义,它将动画链接到滚动的进度。
注意:本文中介绍的功能在撰写本文时浏览器支持有限。最好使用Chrome Canary,但您也可以在 Chrome 115 或更高版本中启用实验性功能,以跟随示例并自己尝试滚动关联动画。
使用动画时间线
在此示例中,我们将实现一个常见的功能:动画化一个简单的进度条,使其在用户滚动网页时从左到右缩放。因为我们要将动画链接到根滚动的进度,所以我们可以使用匿名滚动进度时间线。
首先,让我们定义动画本身。我们希望进度条从左到右缩放,所以我们将使用transform
@keyframes scaleProgress {
0% {
transform: scaleX(0);
}
100% {
transform: scaleX(1);
}
}
为了将进度条元素的动画与滚动的进度关联起来,我们使用了animation-timeline
属性,并将其值设置为scroll()
函数。
.progress {
animation-timeline: scroll();
}
scroll()
函数允许我们指定滚动容器和轴。默认值为 scroll(nearest block)
,这意味着动画将与块轴上最近的可滚动祖先链接。这对于我们的目的已经足够了,尽管我们也可以选择将根指定为滚动容器,因为我们要明确地将动画链接到视窗的滚动进度。
.progress {
animation-timeline: scroll(root block);
}
最后,我们需要将动画添加到进度条元素中,使用我们的关键帧动画作为animation-name
。我们需要将动画持续时间设置为 auto
,因为持续时间将由滚动进度决定。我们还将缓动(animation-timing-function
)设置为 linear
,以便它与滚动同步平滑地进行。如果我们使用默认值 (ease
),动画将从缓慢开始,然后迅速加速,最后在结束时减速——这不是我们从进度指示器中想要的!
.progress {
animation-timeline: scroll(root);
animation-name: scaleProgress;
animation-duration: auto;
animation-timing-function: linear;
}
我们可以使用 animation
简写属性将此内容压缩一些
.progress {
animation: scaleProgress auto linear;
animation-timeline: scroll(root);
}
注意:animation-timeline
目前不包含在简写中。但是,animation
属性会将 animation-timeline
重置为 auto
(默认值),因此我们需要在 animation
简写之后声明 animation-timeline
。
多个动画
就像普通关键帧动画一样,我们可以同时应用多个滚动时间线动画,例如更改进度条的颜色。
.progress {
animation:
scaleProgress auto linear,
colorChange auto linear;
animation-timeline: scroll(root);
}
@keyframes scaleProgress {
0% {
transform: scaleX(0);
}
100% {
transform: scaleX(1);
}
}
@keyframes colorChange {
0% {
background-color: red;
}
50% {
background-color: yellow;
}
100% {
background-color: lime;
}
}
使用不同的缓动函数
尽管我们故意在前面的示例中选择了线性缓动,但我们也可以使用 steps()
缓动来实现一些有趣的效果。此示例显示了一种不同类型的进度条,它使用离散步骤而不是平滑线性缩放。我们在进度条元素上设置了一个线性渐变背景,用于颜色段,然后动画化裁剪路径以依次显示每个段
.progress {
background: linear-gradient(
to right,
red 20%,
orange 0,
orange 40%,
yellow 0,
yellow 60%,
lime 0,
lime 80%,
green 0
);
animation: clip auto steps(5) forwards;
animation-timeline: scroll(root);
}
@keyframes clip {
0% {
clip-path: polygon(0 0, 0 0, 0 100%, 0 100%);
}
100% {
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
}
}
重复和反转动画
滚动进度动画可以与现有的animation-direction
和animation-iteration-count
属性结合使用。因此,我们可以让动画在滚动时间线中重复多次,或反向播放。在这里,当我们滚动时,“球”会弹跳多次。
.progress {
animation: bounce auto linear 6 alternate;
animation-timeline: scroll(root);
}
@keyframes bounce {
100% {
transform: translateY(-50vh);
}
}
定位非祖先滚动容器
有时,我们可能希望动画化一个不是滚动容器后代的元素,但仍然将该元素的动画链接到滚动容器的进度。为此,我们需要创建一个命名滚动进度时间线。我们将通过使用简写scroll-timeline
属性(scroll-timeline-name
和 scroll-timeline-axis
的简写)在滚动容器上声明时间线的名称和轴。同样,块轴是默认值。时间线名称必须以两个连字符作为前缀(类似于自定义属性),这将确保它不会与其他属性值冲突。
滚动容器必须是一个具有滚动能力的元素。
.scroller {
max-height: 300px;
overflow: scroll;
scroll-timeline: --scale-progress block;
}
我们可以使用 animation-timeline
属性将要动画化的元素链接到滚动时间线。
/* Sibling of .scroller */
.progress {
animation: scaleProgress auto linear;
animation-timeline: --scale-progress;
}
@keyframes scaleProgress {
0% {
transform: scaleX(0);
}
100% {
transform: scaleX(1);
}
}
动画化滚动容器的祖先
如果要动画化的元素是滚动容器的兄弟元素,这将起作用。如果我们想要动画化祖先,或兄弟元素的后代,该怎么办?
我们需要另一个 CSS 属性,timeline-scope
,它允许我们修改命名时间线的范围以包含设置它的元素。例如,如果我们在 body
上设置此属性,我们现在可以动画化该元素的背景颜色,即使它已经是滚动容器的祖先。
让我们看一下代码
/* Ancestor element: We want to scope the scroll timeline to include this element and its descendants */
body {
timeline-scope: --scale-progress;
/* Apply the animation */
animation: colorChange auto linear forwards;
animation-timeline: --scale-progress;
}
/* The scroll container on which we declare our timeline */
.scroller {
max-height: 300px;
overflow: scroll;
scroll-timeline: --scale-progress block;
}
/* Apply the animation on the sibling as before */
.progress {
animation: scaleProgress auto linear;
animation-timeline: --scale-progress;
}
注意:timeline-scope
目前仅在 Chrome Canary 和启用了实验性 Web 平台功能的 Chrome 116 中受支持。
探索创意示例
到目前为止,我们创建了一些相当基本的进度条动画——也许这是滚动进度时间线最明显的用例之一。但没有什么能阻止我们对滚动动画进行创意。
水平图像滚动器
使用运动路径
组合多个动画
在此演示中,我们正在滚动时动画化多个元素:文本被显示出来,而盒子从左到右滑动和翻滚。为了简化代码并避免创建多个关键帧,我们正在动画化一个自定义属性,并使用三角函数 计算 translateY
值,这些函数在所有主要浏览器的最新版本中都受支持。与转换属性不同,自定义属性在主线程上动画化,这意味着如果您想动画化大量的自定义属性,您的网站可能会出现性能下降。
无障碍性和用户运动偏好
与任何侵入性动画一样,我们应该始终优先考虑无障碍性,并确保为那些不想使用动画的人关闭动画。对于滚动驱动动画来说,这一点尤其重要,因为即使是通常不会患有前庭疾病的用户,滚动驱动动画也会引起晕动症。如果您想了解更多信息,请查看尊重用户的运动偏好,以了解如何使用prefers-reduced-motion
媒体查询来确保您的动画无障碍。
总结
那么,CSS 中的滚动时间线动画与 JS 库相比如何(一旦它们得到普遍支持)?如果您正在创建特别复杂的动画,您可能仍然需要求助于像GSAP
这样的库,它尤其擅长处理复杂的编排。库也可能为我们提供自定义缓动等功能,而 GSAP 的 Inertia 插件(允许动画在滚动结束后平滑地停止,而不是突然停止)也是如此。目前,我们还没有办法在 CSS 中检测元素是否当前正在滚动。
同样,如果您的动画对用户体验至关重要,您可能需要暂时推迟,因为滚动关联动画可能要过一段时间才能得到普遍支持。
另一方面,如果您需要一些相对简单的滚动驱动动画,CSS 可以为您(和您的用户)节省大量的 JS 负载,从而为您带来巨大的性能提升!
有用资源
- 滚动驱动的动画规范
- 使用滚动驱动的动画在滚动时为元素设置动画 由 Bramus Van Damme