使用网络动画API(WAAPI)和ScrollTimeline的滚动链接动画

284 阅读6分钟

滚动链接动画(Scroll-linked Animations)规范是一个即将到来的实验性的补充,它允许我们将动画进度与滚动进度相链接:当你向上和向下滚动容器时,链接的动画也会相应地前进或倒退。

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

除了我们通过滚动链接动画规范得到的CSS接口外,它还描述了一个实现滚动链接动画的JavaScript接口。让我们来看看ScrollTimeline 类,以及如何用Web Animations API来使用它。

Web Animations API。快速回顾

CSS-Tricks网站上曾经介绍过Web Animations API(WAAPI)。作为一个简短的回顾,该API让我们能够构建动画,并通过JavaScript控制其播放。

以下面这个CSS动画为例,在页面的顶部有一个横条,并且:

  1. reddarkred 的动画,然后
  2. 从零宽到全宽的动画(通过缩放X轴)。

CodePen 嵌入回退

将CSS动画转换为WAAPI的对应代码,代码变成这样:

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

CodePen 嵌入回退

或者,使用更简短的语法,用 Element.animate():

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

CodePen 嵌入回退

在这最后两个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();

CodePen嵌入回退

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

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

CodePen Embed Fallback

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

目前Chromium的实验性实现使用scrollSource ,而不是source 。这就是你在代码例子中看到sourcescrollSource 的原因。

关于浏览器兼容性的话

在写这篇文章的时候,只有Chromium的浏览器支持ScrollTimeline 类,在一个特性标志后面。幸好有Robert Flack的Scroll-Timeline Polyfill,我们可以用它来填补所有其他浏览器不支持的空白。事实上,本文中的所有演示都包含了它。

Polyfill可以作为一个模块使用,如果没有检测到支持,它可以自己注册。要包含它,请在你的JavaScript代码中添加以下import 语句:

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

如果浏览器不支持,Polyfill也会注册所需的CSS类型对象模型类。(👀看着你,Safari。)

高级滚动时间线

除了绝对的偏移,滚动链接的动画也可以与基于元素的偏移一起工作

使用这种类型的滚动偏移,动画是基于滚动容器中的一个元素的位置。

通常情况下,这被用来在一个元素进入滚动端口时为其制作动画,直到它离开滚动端口;例如,当它正在相交时。

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

  1. target:被追踪的DOM元素。
  2. edge:这就是ScrollTimeline'的source ,看着target ,让它穿过。
  3. threshold:一个从0.01.0 的数字,表示targetedge 的滚动端口中的可见程度。(你可能从 IntersectionObserver.)

这里有一个可视化的方法。

CodePen 嵌入偏移

如果你想了解更多关于基于元素的偏移,包括它们如何工作,以及常用偏移的例子,请查看这篇文章

基于元素的偏移也被JSScrollTimeline 接口所支持。要定义一个,使用一个常规对象:

{
  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重制版:当一个图像滚动到视口时,它就会淡入并变成无遮挡的。

CodePen 嵌入回退

更多的例子

这里还有一些我编写的例子。

水平滚动部分

这是以Cameron Knight的演示为基础的,它有一个水平滚动部分。它的行为类似,但使用ScrollTimeline ,而不是GSAP的ScrollTrigger

CodePen嵌入回退

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

CoverFlow

还记得iTunes的CoverFlow吗?嗯,这里有一个用ScrollTimeline:

CodePen嵌入回退

由于一个错误,这个演示在Chromium中的表现并不像预期的那样100%。问题是开始和结束位置的计算不正确。你可以在这个Twitter主题中找到解释(有视频)。

关于这个演示的更多信息可以在这篇文章中找到

使用CSS还是JavaScript?

除了使用的语言外,在滚动链接动画中使用CSS或JavaScript并没有真正的区别:两者都使用相同的概念和结构。本着真正的渐进式增强的精神,我将抓住CSS来实现这些效果。

然而,正如我们前面所提到的,在撰写本文时,对基于CSS的实现的支持是相当差的:

  • Chromium在一个功能标志后面支持它。
  • 火狐正在为它准备一些工作。
  • Safari还没有消息。

由于这种糟糕的支持,在这个非常时刻,你肯定会用JavaScript走得更远。只要确保你的网站在禁用JavaScript时也能被浏览和消费。 😉