使用滚动驱动的动画,在滚动时为元素添加动画效果

了解如何使用滚动时间轴和视图时间轴以声明式方式创建滚动驱动的动画。

发布日期:2023 年 5 月 5 日

滚动驱动的动画

Browser Support

  • Chrome: 115.
  • Edge: 115.
  • Firefox: behind a flag.
  • Safari: 26.

Source

滚动条驱动的动画是 Web 上常见的一种用户体验模式。滚动条驱动的动画与滚动容器的滚动位置相关联。这意味着,当您向上或向下滚动时,关联的动画会直接响应并向前或向后擦除。例如,视差背景图片或随滚动而移动的阅读指示器。

由滚动驱动的文档顶部的阅读指示器。

另一种类似的滚动条驱动的动画是与元素在其滚动容器中的位置相关联的动画。例如,借助它,元素可以在进入视图时淡入。

本页面的图片在进入视图时会淡入。

实现此类效果的经典方法是在主线程上响应滚动事件,但这会导致两个主要问题:

  • 现代浏览器会在单独的进程中执行滚动操作,因此会异步传递滚动事件。
  • 主线程动画容易出现卡顿

这使得创建与滚动同步的高性能滚动条驱动的动画变得不可能或非常困难。

从 Chrome 115 版开始,您可以使用一组新的 API 和概念来启用声明式滚动驱动的动画:滚动时间轴和视图时间轴。

这些新概念与现有的 Web Animations API (WAAPI)CSS Animations API 集成,使它们能够继承这些现有 API 带来的优势。这包括让滚动条驱动的动画在主线程以外运行的功能。没错,您没看错:现在只需添加几行额外的代码,即可实现由滚动驱动的流畅动画,且动画在主线程之外运行。有什么理由不喜欢呢?

网页上的动画,简要回顾

使用 CSS 在网页上制作动画

如需在 CSS 中创建动画,请使用 @keyframes at 规则定义一组关键帧。使用 animation-name 属性将其与某个元素相关联,同时设置 animation-duration 以确定动画应持续多长时间。还有更多 animation-* 详细属性可供使用,例如 animation-easing-functionanimation-fill-mode 等等,这些属性都可以组合到 animation 简写属性中。

例如,以下动画会在沿 X 轴放大元素的同时更改其背景颜色:

@keyframes scale-up {
  from {
    background-color: red;
    transform: scaleX(0);
  }
  to {
    background-color: darkred;
    transform: scaleX(1);
  }
}

#progressbar {
  animation: 2.5s linear forwards scale-up;
}

使用 JavaScript 在网页上添加动画

在 JavaScript 中,可以使用 Web Animations API 来实现完全相同的效果。您可以通过创建新的 AnimationKeyFrameEffect 实例来完成此操作,也可以使用更简短的 Element animate() 方法

document.querySelector('#progressbar').animate(
  {
    backgroundColor: ['red', 'darkred'],
    transform: ['scaleX(0)', 'scaleX(1)'],
  },
  {
    duration: 2500,
    fill: 'forwards',
    easing: 'linear',
   }
);

上述 JavaScript 代码段的视觉效果与之前的 CSS 版本相同。

动画时间轴

默认情况下,附加到元素的动画会在文档时间轴上运行。其初始时间在网页加载时从 0 开始,并随着时钟时间的推移向前递增。这是默认的动画时间轴,也是您之前唯一可以访问的动画时间轴。

滚动驱动的动画规范定义了两种可供您使用的新时间轴类型:

  • 滚动进度时间轴:与滚动容器沿特定轴的滚动位置相关联的时间轴。
  • 查看进度时间轴:与特定元素在其滚动容器中的相对位置相关联的时间轴。

滚动进度时间轴

滚动进度时间轴是与滚动容器(也称为滚动端口滚动条)沿特定轴的滚动位置相关联的动画时间轴。它将滚动范围内的位置转换为进度百分比。

起始滚动位置表示 0% 进度,结束滚动位置表示 100% 进度。在下面的可视化图表中,您可以看到,当您将滚动条从顶部滚动到底部时,进度会从 0% 计数到 100%。

滚动进度时间轴的可视化效果。当您向下滚动到滚动条底部时,进度值会从 0% 递增到 100%。

✨ 亲自尝试一下

滚动进度时间轴通常简称为“滚动时间轴”。

查看进度时间轴

此类时间轴与滚动容器内特定元素的相对进度相关联。与滚动进度时间轴一样,系统会跟踪滚动条的滚动偏移量。与滚动进度时间轴不同,决定进度的是该滚动条中主题的相对位置。

这与 IntersectionObserver 的工作方式有些类似,后者可以跟踪元素在滚动器中的可见程度。如果该元素在滚动条中不可见,则不会交叉。如果它在滚动条内可见(即使是最小部分),则会交叉。

查看进度时间轴从主题开始与滚动条相交开始,到主题停止与滚动条交叉时结束。在下面的可视化图表中,您可以看到,当主题进入滚动容器时,进度会从 0% 开始计数;当主题离开滚动容器的那一刻,进度会达到 100%。

观看进度时间轴的可视化效果。当主题(绿色框)穿过滚动条时,进度会从 0% 增加到 100%。

✨ 亲自尝试一下

查看进度时间轴通常简称为“查看时间轴”。您可以根据拍摄对象的尺寸来定位查看时间轴的特定部分,但我们稍后会详细介绍。

Scroll Progress Timelines 实践

在 CSS 中创建匿名滚动进度时间轴

在 CSS 中创建滚动时间轴的最简单方法是使用 scroll() 函数。此函数将创建一个匿名的滚动时间轴,您可以将其设为新的 animation-timeline 属性的值。

示例:

@keyframes animate-it {  }

.subject {
  animation: animate-it linear;
  animation-timeline: scroll(root block);
}

scroll() 函数接受 <scroller><axis> 参数。

<scroller> 实参的接受值如下:

  • nearest:使用最近的祖先滚动容器(默认)
  • root:使用文档视口作为滚动容器。
  • self:使用元素本身作为滚动容器。

<axis> 实参的接受值如下:

  • block:使用滚动容器的块轴进度测量(默认)
  • inline:使用滚动容器的内轴进度测量。
  • y:使用滚动容器的 y 轴进度测量。
  • x:使用滚动容器的 x 轴进度测量。

例如,如需将动画绑定到块轴上的根滚动器,要传递到 scroll() 中的值是 rootblock。总而言之,该值为 scroll(root block)