使用滚动时间轴在 CSS 中实现滚动链接动画的实际用例

Avatar of Bramus
Bramus

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

滚动链接动画规范 是 CSS 的一项即将推出的实验性功能。得益于 @scroll-timeline at-rule 和 animation-timeline 属性,该规范提供了通过滚动控制常规 CSS 动画的时间位置的功能。

在这篇文章中,我们将探讨一些滚动链接动画派上用场的实际用例,以及它们如何丰富访客的浏览体验。

👨‍🔬 本文介绍的 CSS 功能仍处于实验阶段,尚未最终确定。在撰写本文时,除了 Chromium ≥ 89(启用了“实验性 Web 平台功能”标志)外,任何浏览器都不支持这些功能。

CSS 滚动链接动画,快速入门

使用滚动链接动画规范中描述的功能,您可以通过滚动驱动 CSS 动画:当您向下或向上滚动滚动容器时,链接的 CSS 动画将相应地前进或倒退。这些滚动链接动画可以为您的页面添加非常不错的效果。

虽然已经存在几个用于实现这些滚动链接动画的 JavaScript 库,但滚动链接动画规范与它们的区别在于

  1. 提供了 JS 和 CSS 接口来实现这些效果
  2. 保持高性能,因为动画将在合成器上运行(例如,“主线程之外”

虽然滚动链接动画规范还描述了一个与 Web 动画 API 完美集成的 JavaScript 接口,但本文将仅关注其 CSS 对等项

要在 CSS 中实现基本的滚动链接动画,您需要三个关键部分

  1. CSS 动画
  2. 滚动时间轴
  3. 两者之间的链接

CSS 动画

这与我们已知的常规 CSS 动画相同

@keyframes adjust-progressbar {
  from {
    transform: scaleX(0);
  }
  to {
    transform: scaleX(1);
  }
}

与往常一样,使用 animation 属性 将其附加到元素。

#progressbar {
  animation: 1s linear forwards adjust-progressbar;
}

滚动时间轴

滚动时间轴允许我们将滚动距离映射到动画进度。在 CSS 中,我们使用 CSS @scroll-timeline at-rule 来描述这一点。

@scroll-timeline scroll-in-document-timeline {
  source: auto;
  orientation: vertical;
  scroll-offsets: 0%, 100%;
}

除了为滚动时间轴命名外,还可以使用多个描述符进行配置

  1. source 描述触发时间轴激活并驱动时间轴进度的可滚动元素。默认情况下,它是整个文档(值:auto
  2. orientation 确定应触发动画的滚动方向。默认情况下,它是 vertical
  3. scroll-offsets 属性是一个关键点数组,描述动画应处于活动状态的范围。这些偏移量可以是相对/绝对值(例如百分比和长度)或基于元素的偏移量。

规范的早期版本要求您还设置 time-range 描述符。该描述符已删除,并将自动接管链接动画的 animation-duration。您可能仍然可以在演示中看到该描述符的痕迹,但可以安全地忽略它。

要将我们的 @scroll-timeline 与我们的 CSS 动画关联,我们使用新的 animation-timeline CSS 属性,并使其引用时间轴的名称。

#progressbar {
  animation: 1s linear forwards adjust-progressbar;
  animation-timeline: scroll-in-document-timeline;
}

设置完成后,adjust-progressbar 动画不会在页面加载时自行运行,而只会随着页面的向下滚动而前进。

有关 @scroll-timeline 的更深入介绍,请参阅 第一部分第二部分 我关于滚动链接动画的系列文章。

第一篇文章 更详细地介绍了每个描述符,并通过示例进行解释,然后介绍了更多有趣的演示。

第二篇文章 进行了更深入的探讨,深入研究了基于元素的偏移量,它允许我们在滚动时,随着元素出现在视窗中并消失,驱动动画。

使用基于元素的偏移量,您可以使用 CSS 滚动链接动画实现的效果示例。

实际用例

除了上面介绍的进度条演示外,滚动链接动画还有一些其他用例或场景。

  1. 视差标题
  2. 图像显示/隐藏
  3. 打字动画
  4. 轮播指示器
  5. 滚动侦测

视差标题

滚动链接动画最经典的用例是视差效果,其中页面的多个部分似乎具有不同的滚动速度。只使用 CSS 就可以创建这种类型的效果,但这需要 使用涉及 translate-z()scale() 的令人眼花缭乱的 transform 技巧

受到 Firewatch 标题(它使用上述 transform 技巧)的启发,我创建了这个使用 CSS 滚动时间轴的版本

与原始演示相比

  • 保留了标记,除了不再需要的 .parallax__cover
  • <body> 被赋予了一个 min-height 来创建一些滚动区域。
  • 调整了 .parallax 元素及其 .parallax_layer 子元素的位置。
  • transform/perspective 技巧被滚动时间轴替换。

每个不同的图层都使用相同的滚动时间轴:在 100vh 的距离内滚动。

@scroll-timeline scroll-for-100vh {
  scroll-offsets: 0, 100vh;
}

.parallax__layer {
  animation: 1s parallax linear;
  animation-timeline: scroll-for-100vh;
}

图层之间的不同之处在于,随着我们向下滚动,它们移动的距离

  • 后面的图层应保持静止,例如,移动 0vh
  • 最前面的图层应该移动得最快,例如,100vh
  • 所有中间的图层都进行插值。
@keyframes parallax {
  to {
    transform: translateY(var(--offset));
  }
}

.parallax__layer__0 {
  --offset: 100vh;
}

.parallax__layer__1 {
  --offset: 83vh;
}

.parallax__layer__2 {
  --offset: 67vh;
}

.parallax__layer__3 {
  --offset: 50vh;
}

.parallax__layer__4 {
  --offset: 34vh;
}

.parallax__layer__5 {
  --offset: 17vh;
}

.parallax__layer__6 {
  --offset: 0vh;
}

由于最前面的图层移动的距离更大,因此它们看起来比后面的图层移动得更快,从而实现了视差效果。

图像显示/隐藏

滚动链接动画的另一个很好的用例是图像显示:随着图像滑动进入视野,它将显示出来。

默认情况下,图像的透明度为 0,并使用 clip-path 进行遮罩

#revealing-image {
  opacity: 0;
  clip-path: inset(45% 20% 45% 20%);
}

在最终状态下,我们希望图像完全可见,因此我们将动画的结束帧设置为反映这一点

@keyframes reveal {
  to {
    clip-path: inset(0% 0% 0% 0%);
    opacity: 1;
  }
}

通过使用 基于元素的偏移量 作为滚动时间轴的偏移量,我们可以让显示动画仅在图像本身滑入视野时开始。

@scroll-timeline revealing-image-timeline {
  scroll-offsets:
    selector(#revealing-image) end 0.5,
    selector(#revealing-image) end 1
  ;
}

#revealing-image {
  animation: reveal 1s linear forwards;
  animation-timeline: revealing-image-timeline;
}

😵 无法理解这些基于元素的偏移量?此可视化/工具 可以帮助您。

打字动画

由于 CSS 滚动时间轴可以链接到任何现有的 CSS 动画,因此您可以使用任何 CSS 动画演示并对其进行转换。例如,此打字动画

通过添加滚动时间轴和 animation-timeline 属性,它可以调整为“滚动时打字”

请注意,为了创建一些滚动区域,<body> 的高度也设置为 300vh

使用不同的动画,上面的代码可以轻松调整以创建滚动时缩放效果

我认为这两种效果非常适合文章开头。

轮播(也称为滑块)的组件之一是指示器,它显示轮播包含多少个幻灯片以及当前哪个幻灯片处于活动状态。这通常使用项目符号来完成。

这同样是我们可以使用 CSS 滚动时间线来实现的,如 Fabrizio Calderan 创建的此演示所示

活动状态的项目符号是通过 .slider nav::before 注入的,并设置了使其在其他项目符号上移动的动画

/* Styling of the dots */
.slider nav::before, .slider a {
  inline-size: 1rem;
  aspect-ratio: 1;
  border-radius: 50%;
  background: #9bc;
}

/* Positioning of the active dot */
.slider nav::before {
  content: "";
  position: absolute;
  z-index: 1;
  display: block;
  cursor: not-allowed;
  transform: translateX(0);
  animation: dot 1s steps(1, end) 0s forwards;
}

/* Position over time of the active dot */
@keyframes dot {
  0% 
    { transform: translateX(0); }
  33% 
    { transform: translateX(calc((100% + var(--gap)) * 1)); }
  66% 
    { transform: translateX(calc((100% + var(--gap)) * 2)); } 
  100% 
    { transform: translateX(calc((100% + var(--gap)) * 3)); }
}

通过将 @scroll-timeline 附加到滑块,指示活动状态的圆点可以在您滚动时移动

@scroll-timeline slide {
  source: selector(#s);
  orientation: inline; 
}

.slider nav::before {
  /* etc. */
  animation-timeline: slide;
}

由于动画中包含了 steps() 函数,因此圆点只有在幻灯片滑到其位置后才会移动。当您移除它时,您会更清楚地看到圆点是如何随着您的滚动而移动的

💡 这感觉就像 Christian Shaefer 的纯 CSS 轮播 中最后缺失的部分。

滚动侦察

早在 2020 年初,我就创建了 带滚动活动状态的粘性目录。创建此演示的最后一步是使用 IntersectionObserver 来设置目录 (ToC) 中的活动状态,因为您向上/向下滚动文档。

与上面的轮播指示器演示不同,我们不能仅仅通过移动一个圆点来实现这一点,因为 ToC 中的文字会进行调整。为了解决这种情况,我们需要将两个动画附加到 ToC 中的每个元素上

  1. 第一个动画是在适当的章节出现在文档底部边缘时,在视觉上激活 ToC 项目。
  2. 第二个动画是在适当的章节从文档顶部边缘滑出视野时,在视觉上停用 ToC 项目。
.section-nav li > a {
  animation:
    1s activate-on-enter linear forwards,
    1s deactivate-on-leave linear forwards;
}

由于我们有两个动画,因此我们还需要创建两个滚动时间线,每个内容部分都有两个。以 #introduction 部分为例

@scroll-timeline section-introduction-enter {
  scroll-offsets:
    selector(#introduction) end 0,
    selector(#introduction) end 1;
}

@scroll-timeline section-introduction-leave {
  scroll-offsets:
    selector(#introduction) start 1,
    selector(#introduction) start 0;
}

一旦这两个时间线都链接到这两个动画,一切将按预期工作

.section-nav li > a[href"#introduction"] {
  animation-timeline:
    section-introduction-enter,
    section-introduction-leave;
}

最后

我希望我已经说服您相信滚动链接动画规范所提供的潜力。

不幸的是,它目前仅在基于 Chromium 的浏览器中受支持,隐藏在标志后面。鉴于这种潜力,我个人希望 - 一旦规范确定最终语法 - 其他浏览器供应商也会效仿。

如果您也希望看到滚动链接动画在其他浏览器中实现,您可以积极地关注相关的浏览器问题。

通过积极地关注问题,我们开发人员可以向浏览器供应商表明我们对这些功能的兴趣。