使用 Web Animations API (WAAPI) 和 ScrollTimeline 创建滚动链接动画

Avatar of Bramus
Bramus

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 价值 200 美元的免费积分!

滚动链接动画规范 是一个即将发布的实验性新增功能,它允许我们将动画进度与滚动进度链接:当您向上或向下滚动滚动容器时,链接的动画也会相应地前进或倒退。

我们之前在 CSS-Tricks 上的一篇文章中介绍了一些用例 这些用例都是由 CSS 的 @scroll-timeline at-rule 和 animation-timeline 属性驱动的——没错,所有这些用例都是使用 HTML 和 CSS 构建的。 没有 JavaScript。

除了滚动链接动画规范提供的 CSS 接口之外,它还描述了一个 JavaScript 接口来实现滚动链接动画。 让我们看看 ScrollTimeline 类以及如何将其与 Web Animations API 一起使用。

Web Animations API:快速回顾

Web Animations API (WAAPI) 之前曾在 CSS-Tricks 上介绍过。 作为一个小小的回顾,该 API 允许我们使用 JavaScript 构建动画并控制它们的播放。

例如,以下 CSS 动画,其中一个条形位于页面顶部,并

  1. red 动画到 darkred,然后
  2. 从零宽度动画到全宽度(通过缩放 x 轴)。

将 CSS 动画转换为其 WAAPI 对应物,代码将变为这样

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

或者,可以使用更短的语法,使用 Element.animate()

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

在最后两个 JavaScript 示例中,我们可以区分两件事。 首先,一个 keyframes 对象,它描述了要动画化的属性

{
  backgroundColor: ['red', 'darkred'],
  transform: ['scaleX(0)', 'scaleX(1)'],
}

其次是一个 options 对象,它配置动画持续时间、缓动等。

{
  duration: 2500,
  fill: 'forwards',
  easing: 'linear',
}

创建并附加滚动时间轴

为了让我们的动画由滚动驱动——而不是时钟的单调刻度——我们可以保留现有的 WAAPI 代码,但需要通过将 ScrollTimeline 实例附加到它来扩展它。

这个 ScrollTimeline 类允许我们描述一个 AnimationTimeline,其时间值不是由挂钟时间决定,而是由滚动容器中的滚动进度决定。 它可以使用一些选项进行配置

  • source可滚动的元素,其滚动触发激活并驱动时间轴的进度。 默认情况下,这是 document.scrollingElement(即滚动整个文档的滚动容器)。
  • orientation确定滚动的方向,该方向触发激活并驱动时间轴的进度。 默认情况下,这是 vertical(或作为逻辑值的 block)。
  • scrollOffsets这些决定有效的滚动偏移量,在 orientation 值指定的方向上移动。 它们构成时间轴处于活动状态的进度间隔,这些间隔在进度上是等距的。

这些选项将传递到构造函数中。 例如

const myScrollTimeline = new ScrollTimeline({
  source: document.scrollingElement,
  orientation: 'block',
  scrollOffsets: [
    new CSSUnitValue(0, 'percent'),
    new CSSUnitValue(100, 'percent'),
  ],
});

这些选项与 CSS @scroll-timeline 描述符 完全相同并非巧合。 这两种方法都允许您使用唯一的区别是您用来定义它们的语言来实现相同的结果。

为了将我们新创建的 ScrollTimeline 实例附加到动画,我们将其作为第二个参数传递到 Animation 构造函数中

new Animation(
  new KeyframeEffect(
    document.querySelector('#progress'),
    { transform: ['scaleX(0)', 'scaleX(1)'], },
    { duration: 1, fill: 'forwards' }
  ),
  myScrollTimeline
).play();

当使用 Element.animate() 语法时,将其设置为 options 对象中的 timeline 选项

document.querySelector("#progress").animate(
  {
    transform: ["scaleX(0)", "scaleX(1)"]
  },
  { 
    duration: 1, 
    fill: "forwards", 
    timeline: myScrollTimeline
  }
);

有了这段代码,动画将由我们的 ScrollTimeline 实例驱动,而不是由 默认的 DocumentTimeline 驱动。

Chromium 中当前的实验性实现使用 scrollSource 而不是 source。 这就是您在代码示例中看到 sourcescrollSource 的原因。

关于浏览器兼容性的说明

在撰写本文时,只有 Chromium 浏览器支持 ScrollTimeline 类,但在一个功能标志的后面。 谢天谢地,有一个 由 Robert Flack 提供的 Scroll-Timeline Polyfill,我们可以使用它来填补所有其他浏览器中不支持的空白。 事实上,本文中嵌入的所有演示都包含它。

polyfill 可用作模块,如果未检测到支持,它会自行注册。 要包含它,请将以下 import 语句添加到您的 JavaScript 代码中

import 'https://flackr.github.io/scroll-timeline/dist/scroll-timeline.js';

polyfill 还会注册必要的 CSS Typed Object Model 类,如果浏览器不支持它。 (👀 看看你,Safari。)

高级滚动时间轴

除了绝对偏移量之外,滚动链接动画 也可以使用基于元素的偏移量

使用这种类型的滚动偏移量,动画将基于元素在滚动容器中的位置。

通常,这用于在元素进入视窗直到它离开视窗时对其进行动画处理;例如,在它相交时。

基于元素的偏移量由描述它的三个部分组成

  1. target跟踪的 DOM 元素。
  2. edge这是 ScrollTimelinesource 观察 target 穿过的内容。
  3. threshold一个介于 0.01.0 之间的数字,它表示在 edgetarget 在视窗中可见的比例。(您可能从 IntersectionObserver 中知道这一点。)

这是一个可视化

如果您想了解更多关于基于元素的偏移量的信息,包括它们的工作原理以及常用偏移量的示例,请查看 这篇文章

JS ScrollTimeline 接口也支持基于元素的偏移量。 要定义一个,请使用一个普通对象

{
  target: document.querySelector('#targetEl'),
  edge: 'end',
  threshold: 0.5,
}

通常,您将这两个对象传递到 scrollOffsets 属性中。

const $image = document.querySelector('#myImage');

$image.animate(
  {
    opacity: [0, 1],
    clipPath: ['inset(45% 20% 45% 20%)', 'inset(0% 0% 0% 0%)'],
  },
  {
    duration: 1,
    fill: "both",
    timeline: new ScrollTimeline({
      scrollSource: document.scrollingElement,
      timeRange: 1,
      fill: "both",
      scrollOffsets: [
        { target: $image, edge: 'end', threshold: 0.5 },
        { target: $image, edge: 'end', threshold: 1 },
      ],
    }),
  }
);

这段代码在下面的演示中使用。 它是 我上次介绍的效果 的 JavaScript 重制版:当图像滚动到视窗中时,它会淡入并取消遮盖。

更多示例

以下是一些我制作的更多示例。

水平滚动部分

这是基于 Cameron Knight 的演示,该演示具有水平滚动部分。 它表现得类似,但使用 ScrollTimeline 而不是 GSAP 的 ScrollTrigger

有关此代码工作原理的更多信息以及查看纯 CSS 版本,请参考 这篇文章

CoverFlow

还记得 iTunes 中的 CoverFlow 吗? 好吧,这里有一个使用 ScrollTimeline 构建的版本

由于 一个错误,此演示在 Chromium 中的表现并非完全符合预期。 问题在于开始和结束位置的计算不正确。 您可以在 此 Twitter 线程 中找到解释(带视频)。

您可以在 这篇文章 中找到有关此演示的更多信息。

CSS 还是 JavaScript?

使用 CSS 或 JavaScript 进行滚动链接动画并没有实质区别,只是使用的语言不同:两者都使用相同的概念和结构。秉持渐进增强的精神,我会优先选择 CSS 来实现这种效果。

然而,正如我们之前提到的,截至目前,CSS 实现的浏览器支持率相当低。

由于支持率低下,目前你使用 JavaScript 会更有成效。但请确保你的网站在禁用 JavaScript 的情况下也能正常浏览和使用。😉