教程

用 CSS Scroll-Driven Animations 实现滚动驱动动画:告别 JavaScript 的现代方案

用 CSS Scroll-Driven Animations 实现滚动驱动动画:告别 JavaScript 的现代方案

过去,实现元素随页面滚动而淡入、旋转或滑入等效果,必须依赖 JavaScript 监听滚动事件。现在,CSS 原生支持的 Scroll-Driven Animations(滚动驱动动画)API 彻底改变了这一局面。本教程将带你深入理解这一新 API 的核心概念,从基础的 `animation-timeline: view()` 使用,到自定义动画范围、应用高级缓动函数,并结合实际案例,让你能立刻在项目中创建流畅、高性能的滚动动画。

你将学到
  • 理解 Scroll-Driven Animations 如何将滚动距离映射为动画时间线
  • 掌握 animation-timeline 与 animation-range 属性的核心用法
  • 学会为滚动动画应用自定义缓动(如 linear() 弹簧效果)
  • 了解浏览器支持现状、降级方案及无障碍访问注意事项
  • 通过实际案例拆解,将技巧应用于复杂布局

背景:什么是滚动驱动动画?

在网页设计中,让元素的动画与用户的滚动行为同步,是一种能极大增强体验和个性的手法。例如,一个标题在进入视口时从左侧滑入,或者一张图片在滚动过程中逐渐旋转。

传统上,这类效果必须借助 JavaScript 来监听滚动事件、计算元素位置并手动更新 CSS 属性。 这不仅增加了代码复杂性,也可能对性能造成影响。

如今,情况已经改变。CSS 规范引入了 Scroll-Driven Animations(滚动驱动动画)API。它允许开发者直接使用 CSS 将动画的播放与容器的滚动位置绑定,无需任何 JavaScript。这意味着你可以用纯 CSS 就实现复杂的视差效果、序列动画等。

更重要的是,这个 API 并非完全另起炉灶,而是优雅地构建在现有的 CSS 关键帧动画(@keyframes)基础之上。如果你已经熟悉关键帧动画,那么学习成本将非常低。这代表了 CSS 能力的又一次重大飞跃,值得每一位前端开发者和设计师立即学习。

传统方案 vs. 现代方案
传统 JavaScript 方案监听 `scroll` 事件计算元素与视口的相对位置使用 JS 动态更新 CSS 属性需考虑性能优化(如节流)CSS Scroll-Driven Animat使用 `animation-timeline: view()`浏览器自动处理滚动映射动画由 CSS 引擎原生驱动性能更优,代码更简洁VS传统 JavaScript 方案监听 `scroll` 事件计算元素与视口的相对位置使用 JS 动态更新 CSS 属性需考虑性能优化(如节流)CSS Scroll-Driven An使用 `animation-timeline: view()`浏览器自动处理滚动映射动画由 CSS 引擎原生驱动性能更优,代码更简洁
实现滚动动画的技术路径对比 · NicePSD 原创示意图

准备:开始之前

浏览器支持与工具

目前,该 API 在主流浏览器中已获得广泛支持。根据资料,其在 caniuse 上的支持率约为 85%。Chrome、Edge 和 Safari 均已支持。Firefox 已在 Firefox Nightly 版本中默认启用,在稳定版中可通过修改 about:config 中的 layout.css.scroll-driven-animations.enabled 标志来开启。

在投入生产环境前,务必检查目标用户的浏览器支持情况。 对于不支持的环境,可以考虑使用 Polyfill(资料中提到了一个来自 GitHub 的 polyfill),但需注意,该 polyfill 可能无法完美支持所有高级功能。

所需知识

本教程假设你已经掌握 CSS 关键帧动画(@keyframes)的基础知识,包括如何定义动画、使用 animation 属性及其相关子属性(如 duration, timing-function)。如果不熟悉,建议先学习相关资料。

注意Firefox 用户如需在稳定版中体验,可在地址栏输入 about:config,搜索 layout.css.scroll-driven-animations.enabled 并将其值设为 true。
小技巧对于渐进增强(Progressive Enhancement)项目,可以将滚动驱动动画视为一种视觉增强。即使在不支持的浏览器中,元素仍会正常显示,只是缺少了动画效果。

核心概念:从时间驱动到滚动驱动

这段代码的效果是:浏览器将计算元素在其滚动容器(默认为视口)中的可见进度。当元素的顶部刚进入视口底部时,动画进度为 0%(opacity: 0);当元素的底部离开视口顶部时,动画进度为 100%(opacity: 1)。滚动过程就是在“擦洗”(scrub)这个动画。

view() 是两种滚动时间线类型之一,它基于元素自身与视口的交叉区域(即“视图进度”)来驱动动画。另一种是 scroll(),它基于滚动容器的滚动位置。本教程将主要聚焦于更常用的 view()。

animation-timeline: view() 的进度映射
元素滚动进度0%元素顶部接触视口底部,动画开始50%元素居中于视口100%元素底部离开视口顶部,动画结束元素滚动进度0%元素顶部接触视口底部,动画开始50%元素居中于视口100%元素底部离开视口顶部,动画结束
元素在视口中的位置如何对应动画进度 · NicePSD 原创示意图
.elem {
  animation: fadeIn; /* 注意:这里没有指定 duration */
  animation-timeline: view();
}

1基础动画与时间函数

这样,方块在刚进入视口时旋转得快,接近离开时旋转得慢。你甚至可以使用新的 linear() 函数来创建弹簧物理效果,虽然资料作者开玩笑说这有点“邪门”,但这展示了 API 的强大能力。

.box {
  --super-ease-out: cubic-bezier(0.15, 0.75, 0.35, 1);
  animation: spin var(--super-ease-out);
  animation-timeline: view();
}
常见坑animation 简写属性中通常包含持续时间(如 1s)。但在使用 animation-timeline 时,你通常不在 animation 中指定持续时间,因为进度由滚动控制。如果同时指定,时间线会覆盖持续时间。

2精确控制动画范围(animation-range)

你还可以使用更精确的长度值(如 px、%)来定义范围,例如 animation-range: 20% 80%; 表示动画在视图进度达到 20% 时开始,在 80% 时结束。这提供了极高的控制精度。

animation-range: cover vs contain
cover (默认)动画范围覆盖元素穿过视口的全过程开始:元素顶部接触视口底部结束:元素底部离开视口顶部适合元素始终可见的动画contain动画范围限制在元素完全位于视口内的阶段开始:元素底部接触视口底部(完全进入)结束:元素顶部接触视口顶部(开始离开)适合需要完整展示的入场动画VScover (默认)动画范围覆盖元素穿过视口的全过程开始:元素顶部接触视口底部结束:元素底部离开视口顶部适合元素始终可见的动画contain动画范围限制在元素完全位于视口内的阶段开始:元素底部接触视口底部(完全进入)结束:元素顶部接触视口顶部(开始离开)适合需要完整展示的入场动画
两种范围模式对动画触发时机的影响 · NicePSD 原创示意图
.shape {
  animation: slideIn backwards;
  animation-timeline: view();
  animation-range: contain; /* 动画仅在元素完全在视口内时播放 */
}

3实用技巧与注意事项

动画填充模式(animation-fill-mode)的配合

在关键帧动画中,animation-fill-mode: backwards 可以让元素在动画延迟开始前就应用第一帧的样式,forwards 可以让动画结束后保留最后一帧的样式。这些属性在滚动驱动动画中同样有效,可以帮助处理元素在动画范围之外的状态。

使用 Polyfill

如果需要在不支持的浏览器中提供类似体验,可以考虑使用 Polyfill。资料[2]提供了一个 Polyfill 的使用示例,可以通过 script 标签引入,并同时支持 CSS 和 Web Animations API 的写法。但请注意,Polyfill 可能无法完美实现所有原生功能,且需注意同源策略对 CSS 加载的影响。

@media (prefers-reduced-motion: no-preference) {
  .box {
    animation: spin;
    animation-timeline: view();
  }
}
注意资料[2]中的 Polyfill 示例显示,它同时支持 scroll-timeline 和 view-timeline 的 CSS 语法以及对应的 JavaScript API。

案例分析:马赛国家芭蕾舞团官网

资料[5]展示了一个将滚动驱动动画与创意网页设计完美结合的优秀案例——马赛国家芭蕾舞团(Ballet National de Marseille)的官方网站。

这个网站将浏览器视口当作一个排练舞台,通过“运动网页设计”来呼应舞团的当代运动理念。 虽然资料未明确说明其使用了最新的 Scroll-Driven Animations API(其实现可能基于更早的技术),但其设计理念和效果为我们提供了绝佳的灵感。

设计手法拆解

- 固定导航与粘性面板:左侧固定的粗黑体无衬线导航与右侧带虚线边框的粘性内容卡片形成对比,内容在滚动时层叠堆砌,创造了深度的错觉。

- 3D CSS 文字环:现场文字缩略图使用 3D CSS 变换(transform)将标题弯曲成圆环,并在悬停时旋转。这提示我们,可以将滚动驱动与交互驱动(如 hover)的动画结合起来,创造更丰富的体验。

- 排版与动效:紧密字距的巨型标题与清晰的等宽字体正文形成强烈对比。游览日历摒弃了传统的表格样式,采用带有细线的水平行布局。整个设计在瑞士网格的严谨与 CSS 动画的流畅之间取得了平衡。

对我们的启发

这个案例表明,滚动动画不应是孤立的特效,而应融入整体的排版、布局和交互设计中。你可以尝试:

- 为不同的文本层级(标题、正文、标签)定义不同的滚动动画参数(如 animation-range),创造序列化的出现效果。

- 结合 scroll() 时间线(基于根容器滚动)和 view() 时间线(基于元素自身),在同一个页面上创造多层级的视差效果。

- 始终确保动画服务于内容,并保持界面的清晰可用性。

案例设计要点
视口即舞台将整个滚动视口视为动态表演空间,元素按序入场、退场。排版即动效字体、字距、大小的强烈对比本身就能与滚动动画协同创造节奏感。3D变换增强在滚动驱动的基础上,结合 CSS 3D transform 创造空间感。实用与实验结合在保持日程、导航等功能清晰的前提下,进行视觉实验。视口即舞台将整个滚动视口视为动态表演空间,元素按序入场、退场。排版即动效字体、字距、大小的强烈对比本身就能与滚动动画协同创造节奏感。3D变换增强在滚动驱动的基础上,结合 CSS 3D transform 创造空间感。实用与实验结合在保持日程、导航等功能清晰的前提下,进行视觉实验。
从马赛国家芭蕾舞团官网中可借鉴的设计策略 · NicePSD 原创示意图

对比、选择与进阶

Scroll-Driven Animations vs. JavaScript 方案

何时选择 CSS 方案? 当你需要实现与滚动位置直接、线性相关的动画(如淡入、滑入、旋转),且动画逻辑不特别复杂时,CSS API 是首选。它性能更好,代码更简洁,维护成本低。

何时仍需 JavaScript? 当动画逻辑需要复杂的计算(如基于滚动速度、方向变化)、或需要与非滚动事件深度交互时,JavaScript 配合 Web Animations API 或动画库可能更灵活。

两种时间线:view() 与 scroll()

本教程重点介绍了 view(),它基于元素与视口的交叉区域。另一种 scroll() 时间线则基于指定的滚动容器的滚动位置百分比。scroll() 更适合创建传统的、基于整个页面滚动的视差背景效果。你可以根据具体效果选择。

进阶技巧:链接时间线(Linked Timelines)

资料[1]提到了“链接时间线”这个更高级的功能。它允许一个元素的动画进度去驱动另一个元素的动画进度,从而创建出复杂的连锁反应效果。这标志着滚动驱动动画从“元素自身运动”进入了“元素间运动协调”的新阶段,值得深入探索。

特性CSS Scroll-Driven Animations传统 JavaScript 方案
代码复杂度低(纯CSS声明)中到高(需编写JS逻辑)
性能高(由浏览器引擎优化)取决于实现(需注意节流/防抖)
控制精度高(支持百分比、长度值范围)极高(可编程任意逻辑)
浏览器支持逐渐普及(约85%,Firefox需标志)universally supported
学习曲线低(基于已知的CSS动画)中(需熟悉滚动事件与DOM API)
小技巧在项目初期,可以先用 CSS 方案实现核心的滚动视觉效果,对于个别极端定制化的效果,再用 JavaScript 作为补充。这种混合策略往往最高效。

总结

CSS Scroll-Driven Animations 是一个改变游戏规则的 API。它将曾经需要复杂 JavaScript 才能实现的滚动交互效果,变成了几句清晰的 CSS 声明。通过将 @keyframes 动画与 animation-timeline(特别是 view())和 animation-range 属性结合,你可以轻松创建出响应式、高性能且易于维护的滚动动画。

从今天开始,你可以将滚动驱动动画视为你的默认工具之一,用于增强页面个性、引导用户视线和创造沉浸式叙事。 记住从简单的淡入淡出开始,逐步尝试控制动画范围和应用自定义缓动。同时,永远不要忘记无障碍访问,用 prefers-reduced-motion 媒体查询保护好你的用户。

随着浏览器支持的不断巩固和更多高级功能(如链接时间线)的普及,纯 CSS 创造复杂滚动体验的边界还将不断拓展。现在正是学习和实验的最佳时机。

要点速览

  • Scroll-Driven Animations 的核心是将动画进度映射到滚动距离,而非时间,通过 animation-timeline: view() 实现。
  • 使用 animation-range 属性(如 contain)可以精确控制动画在滚动过程中的触发区间,确保动画完整可见。
  • 滚动动画支持所有标准的 CSS 缓动函数,包括 cubic-bezier() 和新的 linear() 弹簧函数,让运动更自然。
  • 务必用 @media (prefers-reduced-motion) 包裹动画代码,尊重用户的运动偏好设置,这是无障碍开发的基本要求。
  • 该 API 与现有 CSS 关键帧动画知识体系无缝衔接,学习成本低,是实现渐进增强视觉效果的利器。
  • 对于复杂或需要高度程序化控制的滚动效果,JavaScript 仍是备选方案,但多数常见效果现已可用纯 CSS 高效完成。

参考来源

  1. Scroll-Driven Animations Josh W. Comeau · Josh W. Comeau
  2. GitHub - flackr/scroll-timeline: A polyfill of ScrollTimeline. GitHub
  3. Experimental features in Firefox - Mozilla | MDN MDN Web Docs
  4. An Interactive Guide to CSS Keyframe Animations with @keyframes • Josh W. Comeau joshwcomeau.com · Josh W. Comeau
  5. Ballet National de Marseille: Kinetic Web Design & CSS Type Abduzeedo

封面为 NicePSD 原创设计,未使用原文图片;封面中出现的产品名称和标志归各自权利人所有,仅用于标明文中讨论的工具,不代表合作或认可。原创声明:本文由 NicePSD 基于海外公开资料整理、消化并重写,观点归原作者所有,详细内容请阅读参考原文。转载请注明出处。

更多前沿文章