滚动链接动画规范 是一个即将发布的实验性新增功能,它允许我们将动画进度与滚动进度链接:当您向上或向下滚动滚动容器时,链接的动画也会相应地前进或倒退。
我们之前在 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 动画,其中一个条形位于页面顶部,并
- 从
red
动画到darkred
,然后 - 从零宽度动画到全宽度(通过缩放 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
。 这就是您在代码示例中看到 source
和 scrollSource
的原因。
关于浏览器兼容性的说明
在撰写本文时,只有 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。)
高级滚动时间轴
除了绝对偏移量之外,滚动链接动画 也可以使用基于元素的偏移量
使用这种类型的滚动偏移量,动画将基于元素在滚动容器中的位置。
通常,这用于在元素进入视窗直到它离开视窗时对其进行动画处理;例如,在它相交时。
基于元素的偏移量由描述它的三个部分组成
target
:跟踪的 DOM 元素。edge
:这是ScrollTimeline
的source
观察target
穿过的内容。threshold
:一个介于0.0
到1.0
之间的数字,它表示在edge
时target
在视窗中可见的比例。(您可能从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 实现的浏览器支持率相当低。
- Chromium 在特性标识符(feature flag)下支持它。
- Firefox 正在进行相关准备工作。(Mozilla 问题单 #1676780)
- Safari 目前尚未有任何消息。(WebKit 问题单 #222295)
由于支持率低下,目前你使用 JavaScript 会更有成效。但请确保你的网站在禁用 JavaScript 的情况下也能正常浏览和使用。😉
当我尝试使用 “new ScrollTimeline” 时,代码报错提示 ScrollTimeline 未定义。这是什么原因导致的,如何解决呢?