滚动链接动画通常能为网站增添一丝优雅,但长期以来一直是 JavaScript 的专利。现在,一项全新的规范正在实施,使我们能够使用 CSS 创建丰富的滚动驱动体验!
当我们想到滚动驱动的动画时,我们通常意味着以下两件事之一:
- 用户滚动时发生的动画,其进度与滚动进度明确关联。例如,长篇文章的进度条。
- 当元素进入、退出或穿过可见区域(通常是视口,但也可能是另一个可滚动容器的可见部分(定义为滚动口))时发生的动画。
滚动驱动动画规范涵盖了这两种类型的动画。在本文中,我们将首先介绍滚动进度时间轴,顾名思义,它将动画与滚动进度联系起来。
使用动画时间轴
在这个例子中,我们将实现一个常见的功能:为一个简单的进度条添加动画效果,使其随着用户滚动网页从左向右缩放。由于我们希望将动画与根滚动条的进度关联起来,因此可以使用匿名滚动进度时间轴。
首先我们来定义动画本身。我们希望进度条从左向右缩放,因此我们将使用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-timeline
后animation
声明。
多个动画
就像常规关键帧动画一样,我们可以同时应用多个滚动时间轴动画,例如更改进度条的颜色。
.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()
缓动实现一些有趣的效果。此例展示了一种不同类型的进度条,它采用离散步长而非平滑的线性缩放。我们在进度条元素上为颜色段设置了线性渐变背景,然后通过 clip-path 动画依次显示每个颜色段:
.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);
}
}
定位非祖先滚动容器
有时,我们可能希望为非滚动容器后代元素添加动画,但仍将该元素的动画与滚动容器的进度关联起来。为此,我们需要创建一个命名的滚动进度时间轴。我们将使用简写属性( andscroll-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 和启用了实验性网络平台功能的 Chrome 116。
探索创意示例
到目前为止,我们已经创建了一些相当基本的进度条动画——这或许是滚动进度时间轴较为常见的用例之一。但我们仍然可以发挥创意,打造更具创意的滚动动画。
水平图像滚动条
在用户垂直滚动时水平移动元素,可以让网页更具动感,减少线性感。这里我们为一排图片添加动画效果,让它们在用户垂直滚动时从左侧滑入。
使用运动路径
我们可以在 CSS 中通过offset-path
定义元素的运动路径来定位和动画元素,使其沿着路径移动。这是一种比矩形进度条更有趣的进度指示方式!
组合多个动画
在这个演示中,我们为多个元素的滚动添加动画:文本显示,同时框从左向右滑动并翻转。为了简化代码并避免创建多个关键帧,我们为一个自定义属性添加动画,并translateY
使用三角函数计算其值,所有主流浏览器的最新版本都支持该函数。与变换属性不同,自定义属性在主线程上进行动画处理,这意味着如果您尝试为大量自定义属性添加动画,您的网站性能可能会受到影响。
可访问性和用户运动偏好
与任何侵入式动画一样,我们应始终优先考虑可访问性,并确保为那些不想使用动画的用户关闭动画。这对于滚动驱动的动画尤其重要,因为即使通常没有前庭功能障碍的用户,滚动驱动的动画也会引起晕动症。如果您想了解更多信息,请参阅“尊重用户的动作偏好”,了解如何使用prefers-reduced-motion
媒体查询来确保您的动画易于访问。
概括
那么,CSS 中的滚动时间轴动画与 JS 库(一旦它们得到普遍支持)相比如何?如果您要创建特别复杂的动画,可能仍然需要使用像 这样的库GSAP
,它特别适合处理复杂的编排。库还可以为我们提供自定义缓动和 GSAP 的 Inertia 插件(允许动画在滚动完成后滑动停止,而不是突然停止)等功能。目前,我们还没有办法在 CSS 中检测元素当前是否正在滚动。
同样,如果您的动画对于用户体验至关重要,您可能需要暂时推迟,因为可能需要一段时间才能普遍支持滚动链接动画。
另一方面,如果您需要一些相对简单的滚动驱动动画,CSS 可以为您(和您的用户)节省大量 JS 负载,从而为您带来巨大的性能提升!