开始在 CSS 中使用滚动驱动的动画

1. 准备工作

借助滚动驱动的动画,您可以根据滚动容器的滚动位置控制动画的播放。这意味着,当您向上或向下滚动时,动画会向前或向后拖动。此外,借助滚动驱动的动画,您还可以根据元素在其滚动容器中的位置来控制动画。这样一来,您就可以制作有趣的效果,例如视差背景图片、滚动进度条,以及在进入视图时显示的图片。

Chrome 114 中的新功能支持一组 JavaScript 类和 CSS 属性,可让您轻松创建声明式滚动驱动的动画。这些新 API 可与现有的 Web Animations 和 CSS Animations API 结合使用。

在此 Codelab 中,您将学习如何使用 CSS 创建滚动驱动的动画。完成本 Codelab 后,您将熟悉这项激动人心的功能引入的许多新 CSS 属性,例如 scroll-timelineview-timelineanimation-timelineanimation-range

学习内容

  • 如何在 CSS 中使用滚动时间轴创建视差背景效果。
  • 如何在 CSS 中通过滚动时间轴创建进度条。
  • 如何在 CSS 中使用视图时间轴创建图片显示效果。
  • 如何在 CSS 中定位不同类型的视图时间轴。

所需条件

以下设备组合之一:

  • 适用于 ChromeOS、macOS 或 Windows 的最新版 Chrome(114 或更高版本)
  • 对 HTML 有基本的了解
  • 对 CSS(尤其是 CSS 中的动画)有基本的了解

2. 进行设置

您可以在 GitHub 代码库中找到所需的有关此项目的所有内容。首先,请克隆代码并在您常用的开发环境中打开。

  1. 打开新的浏览器标签页,然后转到 https://github.com/googlechromelabs/io23-scroll-driven-animations-codelab
  2. 克隆代码库。
  3. 在您的首选 IDE 中打开该代码。
  4. 运行 npm install 以安装依赖项。
  5. 运行 npm start 并访问 http://localhost:3000/
  6. 或者,如果您尚未安装 npm,请在 Chrome 中打开 src/index.html 文件。

3. 了解动画时间轴

默认情况下,附加到元素的动画会在文档时间轴上运行。这意味着,网页加载时,动画会随着时间的推进而前进。这是默认的动画时间轴,到目前为止,这是您有权访问的唯一动画时间轴。

借助滚动驱动的动画,您可以访问两种新的时间轴:

  • 滚动进度时间轴
  • 查看进度时间轴

在 CSS 中,可以使用 animation-timeline 属性将这些时间轴附加到动画。了解这些新时间轴的含义以及差异。

滚动进度时间轴

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

开始滚动位置表示进度为 0%,结束滚动位置表示进度为 100%。在下面的可视化图表中,请注意,当您向下滚动滚动条时,进度会从 0% 增加到 100%。

查看进度时间轴

这种类型的时间轴与滚动容器中特定元素的相对进度相关联。与滚动进度时间轴一样,系统会跟踪滚动条的滚动偏移量。与滚动进度时间轴不同,决定进度的是该滚动条中主题的相对位置。这与 IntersectionObserver 不同,后者用于跟踪元素在滚动条中的可见程度。如果该元素在滚动条中不可见,则不会交叉。如果它在滚动条内可见(即使是最小部分),则会交叉。

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

默认情况下,与查看进度时间轴关联的动画会附加到其整个范围。此动画从主题进入滚动端口的那一刻开始,到主题离开滚动端口时结束。

此外,您还可以指定其应附加到的范围,从而将其关联到“查看进度时间轴”的特定部分。例如,这可以是仅当主题进入滚动条时的情况。在下面的可视化图表中,当主题进入滚动容器时,进度会从 0% 开始计数,但从完全交叉的那一刻起,进度便达到 100%。

您可定位的查看时间轴范围包括 covercontainentryexitentry-crossingexit-crossing。此 Codelab 稍后将介绍这些范围,如果您迫不及待,请使用 https://goo.gle/view-timeline-range-tool 中的工具查看每个范围代表的内容。

4. 创建视差背景效果

添加到页面的第一种效果是主背景图片上的视差背景效果。在页面中向下滚动时,背景图片应该会移动,只是速度不同。为此,您需要依赖滚动进度时间轴。

如需实现这一点,需要完成两个步骤:

  1. 创建一个用于移动背景图片位置的动画。
  2. 将动画与文档的滚动进度相关联。

创建动画

  1. 如需创建动画,请使用一组常规关键帧。在其中,将背景位置从 0% 垂直移动到 100%:

src/styles.css

@keyframes move-background {
  from {
    background-position: 50% 0%;
  }
  to {
    background-position: 50% 100%;
  }
}
  1. 现在,将以下关键帧附加到正文元素:

src/styles.css

body {
  animation: 1s linear move-background;
}

使用此代码,move-background 动画会添加到正文元素中。其 animation-duration 属性设置为 1 秒,并使用 linear 加/减速选项。

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

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

<scroller> 参数的可接受值如下:

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

<axis> 参数的可接受值如下:

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

如要将动画与块轴上的根滚动条相关联,要传递到 scroll() 的值为 rootblockscroll(root block)

  • scroll(root block) 设为正文中 animation-timeline 属性的值。此外,由于以秒为单位表示的 animation-duration 没有意义,因此请将时长设为 auto。如果未指定 animation-duration,则默认为 auto

src/styles.css

body {
  animation: linear move-background;
  animation-duration: auto;
  animation-timeline: scroll(root block);
}

由于根滚动条恰好是正文元素的最接近的父滚动条,因此您还可以使用值 nearest

src/styles.css

body {
  animation: linear move-background;
  animation-duration: auto;
  animation-timeline: scroll(nearest block);
}

由于 nearestblock 是默认值,因此您可以选择省略它们。在这种情况下,代码可以简化为:

src/styles.css

body {
  animation: linear move-background;
  animation-duration: auto;
  animation-timeline: scroll();
}

验证您的更改

如果一切顺利,您现在应该拥有了以下代码:

否则,请查看代码的 solution-step-1 分支

5. 为图库创建进度条

页面上是一个横向轮播界面,需要使用进度条来指明您当前正在查看哪张照片。

轮播界面的标记如下所示:

src/index.html

<div class="gallery">
  <div class="gallery__scrollcontainer" style="--num-images: 3;">
    <div class="gallery__progress"></div>
    <div class="gallery__entry">
      ...
    </div>
    <div class="gallery__entry">
      ...
    </div>
    <div class="gallery__entry">
      ...
    </div>
  </div>
</div>

进度条的关键帧已经放置,如下所示:

src/styles.css

@keyframes adjust-progress {
  from {
    transform: scaleX(calc(1 / var(--num-images)));
  }
  to {
    transform: scaleX(1);
  }
}

此动画需要附加到带有滚动进度时间轴的 .gallery__progress 元素。如上一步所示,您可以使用 scroll() 函数创建匿名滚动进度时间轴,进而实现这一点:

src/styles.css

.gallery__progress {
  animation: linear adjust-progress;
  animation-duration: auto;
  animation-timeline: scroll(nearest inline);
}

定义滚动进度时间轴的另一种方法是使用命名的时间轴。虽然这种方法有点繁琐,但当您没有定位父滚动条或根滚动条或者网页使用多个时间轴时,这种方法可能会很有用。这样,您就可以通过指定名称来标识滚动进度时间轴。

若要为某个元素创建命名的滚动进度时间轴,请将该滚动容器上的 scroll-timeline-name CSS 属性设为您期望的值。

由于图库会水平滚动,因此您还需要设置 scroll-timeline-axis 属性。允许的值与 scroll()<axis> 参数相同。

最后,若要将动画与滚动进度时间轴相关联,请将需要添加动画效果的元素上的 animation-timeline 属性设置为与 scroll-timeline-name 所用的标识符相同的值。

  • 更改 styles.css 文件以包含以下内容:

src/styles.css

.gallery__scrollcontainer {
  /* Create the gallery-is-scrolling timeline */
  scroll-timeline-name: gallery-is-scrolling;
  scroll-timeline-axis: inline;
}

.gallery__progress {
  animation: linear adjust-progress;
  animation-duration: auto;
  /* Set gallery-is-scrolling as the timeline */
  animation-timeline: gallery-is-scrolling;
}

验证您的更改

如果一切顺利,您现在应该拥有了以下代码:

否则,请查看代码的 solution-step-2 分支

6. 在图库图片进入和退出滚动端口时为其添加动画效果

设置匿名查看进度时间轴

可以添加的一个良好效果是让图库图片淡入视图。为此,您可以使用查看进度时间轴。

如需创建查看进度时间轴,您可以使用 view() 函数。它接受的参数为 <axis><view-timeline-inset>

  • <axis> 与滚动进度时间轴相同,并定义要跟踪的轴。
  • 借助 <view-timeline-inset>,您可以指定偏移量(正值或负数值),以便在元素被视为是否在视图范围内时调整边界。
  • 关键帧已经存在,您只需附加即可。为此,请为每个 .gallery__entry 元素创建一个查看进度时间轴。

src/styles.css

@keyframes animate-in {
  from {
    opacity: 0;
    clip-path: inset(50% 0% 50% 0%);
  }
  to {
    opacity: 1;
    clip-path: inset(0% 0% 0% 0%);
  }
}

.gallery__entry {
  animation: linear animate-in;
  animation-duration: auto;
  animation-timeline: view(inline);
}

限制查看进度时间轴的范围

如果您保存 CSS 并加载网页,会看到元素淡入,但似乎有些不正常。当它们完全不可见时,会以不透明度 0 开始;当它们完全退出时,只会以不透明度 1 结束。

这是因为查看进度时间轴的默认范围是整个范围。这称为 cover 范围。

  1. 若要仅定位主题的 entry 范围,请使用 animation-range CSS 属性,以限制动画的运行时间。

src/styles.css

.gallery__entry {
  animation: linear fade-in;
  animation-duration: auto;
  animation-timeline: view(inline);
  animation-range: entry 0% entry 100%;
}

动画现在从 entry 0%(主题即将进入滚动条)运行到 entry 100%(主题已完全进入滚动条)。

可能的查看时间轴范围如下:

  • cover。表示查看进度时间轴的完整范围。
  • entry。表示主框进入查看进度可见性范围的范围。
  • exit。表示主框退出查看进度可见性范围的范围。
  • entry-crossing。表示主框跨越结束边框边缘的范围。
  • exit-crossing。表示主框跨越起始边框边缘的范围。
  • contain。表示主框完全位于或完全覆盖滚动端口内查看进度可见性范围的范围。这取决于主题比滚动条更高还是更短。

使用位于 https://goo.gle/view-timeline-range-tool 的工具可查看每个范围代表什么,以及百分比对起始位置和结束位置有何影响。

  1. 由于此处的起始范围和结束范围相同且使用默认偏移量,因此请将 animation-range 简化为单个动画范围名称:

src/styles.css

.gallery__entry {
  animation: linear animate-in;
  animation-duration: auto;
  animation-timeline: view(inline);
  animation-range: entry;
}
  • 如要在图片退出滚动条时淡出图片,您可以执行与动画效果相同的动画,但定位不同的范围。

src/styles.css

@keyframes animate-out {
  from {
    opacity: 1;
    clip-path: inset(0% 0% 0% 0%);
  }
  to {
    opacity: 0;
    clip-path: inset(50% 0% 50% 0%);
  }
}

.gallery__entry {
  animation: linear animate-in, linear animate-out;
  animation-duration: auto;
  animation-timeline: view(inline);
  animation-range: entry, exit;
}

animate-in 关键帧将应用于 entry 范围,animate-out 关键帧将应用于 exit 范围。

验证您的更改

如果一切顺利,您现在应该拥有了以下代码:

否则,请查看代码的 solution-step-3 分支

7. 使用一组关键帧,在图库图片进入和退出滚动端口时为其添加动画效果

一组关键帧的情况

您可以不必将两个动画附加到不同的范围,而是创建一组包含范围信息的关键帧。

关键帧的形式如下所示:

@keyframes keyframes-name {
  range-name range-offset {
    ...
  }
  range-name range-offset {
    ...
  }
}
  1. 结合使用淡入和淡出关键帧,如下所示:

src/styles.css

@keyframes animate-in-and-out {
  entry 0% {
    opacity: 0;
    clip-path: inset(50% 0% 50% 0%);
  }
  entry 90% {
    opacity: 1;
    clip-path: inset(0% 0% 0% 0%);
  }

  exit 10% {
    opacity: 1;
    clip-path: inset(0% 0% 0% 0%);
  }
  exit 100% {
    opacity: 0;
    clip-path: inset(50% 0% 50% 0%);
  }
}
  1. 当关键帧中包含范围信息时,您无需再单独指定 animation-range。以 animation 属性的形式附加关键帧。

src/styles.css

.gallery__entry {
  animation: linear animate-in-and-out both;
  animation-duration: auto;
  animation-timeline: view(inline);
}

验证您的更改

如果一切顺利,您应该会看到与上一步相同的结果。否则,请查看代码的 solution-step-4 分支

8. 恭喜!

您已完成此 Codelab,现已了解如何在 CSS 中创建滚动进度时间轴和查看进度时间轴!

了解详情

资源: