当前位置:首页 > 文章列表 > 文章 > 前端 > CSS scroll-driven animations 绑定滚动进度

CSS scroll-driven animations 绑定滚动进度

来源:17golang原创 2026-10-10 18:36:55 0浏览 收藏
热门推荐
漫画APP
动画内容聚合,热门资源快捷查看
立即下载

如果页面上有阅读进度条、滚动时逐渐出现的卡片,很多旧实现都会在 scroll 事件里读取滚动距离,再手动算出百分比并写回样式。CSS scroll-driven animations 提供了更直接的做法:让 @keyframes 的进度由滚动时间线控制,而不是由时间流逝控制。这样动画的当前位置天然对应滚动位置,向上滚动时也会反向回退。

这篇文章只解决一个问题:如何把 CSS 动画绑定到滚动进度。你会看到 scroll()、view() 和命名时间线的区别,也会知道为什么 animation-timeline 要写在 animation 简写之后。

先看旧方案:scroll 事件真正麻烦在哪里

假设要做一个页面顶部进度条,传统写法需要监听滚动事件、取得文档高度、计算当前百分比,再把结果写进 CSS 变量。它能工作,但业务代码需要自己处理滚动容器、节流、边界值和主线程上的样式更新。

const progress = document.querySelector('.reading-progress');

function updateProgress() {
  // 计算页面可滚动的总距离,避免把视口高度重复算入分母。
  const maxScroll = document.documentElement.scrollHeight - window.innerHeight;
  const ratio = maxScroll > 0 ? window.scrollY / maxScroll : 0;
  // 将范围限制在 0 到 1,防止橡皮筋滚动产生异常值。
  progress.style.transform = `scaleX(${Math.min(1, Math.max(0, ratio))})`;
}

window.addEventListener('scroll', updateProgress, { passive: true });
updateProgress();

问题不在于这段代码一定错误,而在于它把“滚动位置如何驱动动画”变成了 JavaScript 维护的状态。CSS 时间线可以把这层关系交给样式系统,让脚本只负责真正的业务交互。

背景:滚动时间线和普通动画时间线不同

普通 CSS 动画默认使用基于时间的文档时间线,animation-duration 决定它在多少秒内完成。滚动驱动动画改用滚动进度时间线或视图进度时间线:滚动条走到哪里,关键帧就推进到哪里;滚动暂停,动画也停在当前进度。

这里要区分两个相近概念:

  • scroll-driven animation:动画本身的时间线由滚动或可见进度控制,会随着滚动前进和后退。
  • scroll-triggered animation:仍然是普通的时间动画,只是在元素到达某个滚动位置时开始或停止。

本文讨论第一种。选错概念时,常见结果是动画只触发一次,却没有和滚动位置保持连续对应。

滚动容器通过命名时间线控制 CSS 关键帧动画的结构说明图
图1:命名滚动时间线与关键帧动画的结构示意图,不是运行截图。

第一种写法:用 scroll() 绑定匿名滚动进度

最小写法是给元素声明一个关键帧动画,再使用 animation-timeline: scroll()。scroll() 可以接收滚动容器和轴向参数,例如 scroll(nearest block) 表示沿最近的块轴滚动容器取进度。

一篇有滚动进度的文章

这里放足够长的正文,让滚动范围真实存在。

.reader-shell {
  /* 让示例拥有独立的纵向滚动容器。 */
  height: 28rem;
  overflow-y: auto;
}

.reading-progress {
  /* 缩放原点放在左侧,进度从左向右增长。 */
  position: sticky;
  inset-block-start: 0;
  z-index: 1;
  height: 0.35rem;
  background: #2563eb;
  transform-origin: left center;
  transform: scaleX(0);
  animation: fill-progress linear both;
  /* timeline 写在 animation 简写之后,避免被简写重置。 */
  animation-timeline: scroll(nearest block);
}

@keyframes fill-progress {
  /* 关键帧只描述视觉变化,进度交给滚动时间线。 */
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

这段代码里没有设置秒数,因为动画进度不再由秒数推动。linear 保持滚动百分比和关键帧百分比的对应关系,both 让元素在时间线之外也保留关键帧的起止样式。

第二种写法:命名 scroll-timeline 让关系更容易复用

当同一个滚动容器需要控制多个动画时,可以先给容器命名时间线,再让后代元素通过 animation-timeline 引用它。命名方式适合组件化场景,因为“谁提供进度”和“谁消费进度”会分开表达。

.chapter-list {
  /* 暴露一个可被后代引用的纵向滚动时间线。 */
  scroll-timeline-name: --chapter-scroll;
  scroll-timeline-axis: block;
  overflow-y: auto;
  height: 24rem;
}

.chapter-list .chapter-marker {
  /* 这个元素消费容器提供的命名时间线。 */
  animation: marker-shift linear both;
  animation-timeline: --chapter-scroll;
}

@keyframes marker-shift {
  /* 标记随章节滚动从透明移动到可见位置。 */
  from {
    opacity: 0.25;
    transform: translateX(-0.75rem);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

命名时间线的关键是作用域。滚动容器必须真的能滚动,动画元素必须位于能看到这条时间线的关系范围内;如果容器没有溢出内容,时间线就没有可推进的有效范围。

第三种写法:view() 关注元素进入视口的范围

scroll() 关注的是滚动容器从起点到终点的整体进度;view() 关注的是某个元素在滚动视口中的可见进度。做“卡片进入视口时逐渐显现”“章节标题经过视口中线时完成强调”时,view() 更符合问题本身。

scroll 和 view 两种 CSS 滚动时间线的范围对比说明图
图2:scroll() 与 view() 的进度范围对比示意图,不是运行截图。
.feature-card {
  /* animation 简写先声明动画,再指定由元素可见范围驱动。 */
  animation: reveal-card linear both;
  animation-timeline: view(block);
  /* 缩小有效范围,让卡片靠近视口中央时才进入主要动画段。 */
  animation-range: entry 15% cover 45%;
}

@keyframes reveal-card {
  /* 卡片进入可见范围时,从轻微位移过渡到正常状态。 */
  from {
    opacity: 0;
    transform: translateY(1.25rem);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

animation-range 控制关键帧映射到时间线的哪一段。它不是给动画加一个延迟计时器,而是改变元素进入、覆盖视口等范围与关键帧之间的对应关系。向上滚动时,元素离开视口,动画也会反向回退。

代码不生效时,按这张表排查

现象优先检查处理方式
动画像普通时间动画一样自动播放是否漏写 animation-timeline在 animation 简写之后补上 scroll()、view() 或命名时间线
滚动但完全没有进度滚动容器是否有 overflow 和实际溢出内容确认 scrollHeight 大于容器可视高度
命名时间线找不到名称和作用域是否一致核对 --chapter-scroll 拼写、容器层级和轴向设置
卡片只在第一次出现时动一下是否误用了 scroll-triggered 思路改用 view() 时间线,并按可见范围映射关键帧
简写后 timeline 没效果声明顺序把 animation-timeline 放到 animation 之后

兼容与无障碍:把它当作渐进增强

MDN 当前将 animation-timeline、scroll() 等能力标记为 Limited availability,说明它们还不是所有主流浏览器都一致支持。生产页面不要把关键内容的可见性完全押在这组动画上:内容默认应该可读,动画只是增强。

可以先用静态样式保证布局,再通过特性查询启用滚动动画,并尊重用户的减少动态效果偏好:

.feature-card {
  /* 不支持滚动时间线时,卡片仍然保持可读的默认状态。 */
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .feature-card {
      /* 只有浏览器支持且用户未要求减少动态效果时才增强。 */
      animation: reveal-card linear both;
      animation-timeline: view(block);
      animation-range: entry 15% cover 45%;
    }
  }
}

如果业务必须支持不具备该能力的环境,再补一个轻量 JavaScript 回退即可;回退应只负责改变一个 CSS 类或变量,不要和 CSS 主路径同时争夺同一个动画属性。

最后的采用建议

先判断你要表达的是“整个容器滚了多少”,还是“某个元素进入视口多少”。前者优先选择 scroll() 或命名 scroll-timeline,后者优先选择 view()。然后按“默认可读、动画可选、减少动态效果优先”的顺序组织样式,最后在目标浏览器矩阵中实际确认支持情况。

CSS scroll-driven animations 的价值不是把所有滚动交互都改成 CSS,而是让连续的视觉进度回到声明式时间线中。这样代码更接近设计意图,也更容易把滚动容器、可见范围和关键帧之间的关系讲清楚。

相关问题

scroll() 和 view() 可以同时使用吗?

可以。它们可以服务于同一页面中的不同元素:整体阅读进度适合 scroll(),卡片进入视口的显现适合 view()。不要让一个元素同时依赖两条互相冲突的时间线。

为什么 animation-duration 在滚动驱动动画里不重要?

因为动画进度由滚动时间线推进,而不是由经过几秒推进。应该把注意力放在时间线来源和 animation-range 上。

不支持 scroll-driven animations 时内容会消失吗?

只要默认样式保持可读就不会。不要用只在关键帧末端才出现的状态承载正文信息,把动画当作渐进增强。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
cgroup v2 io.weight 调整块设备相对优先级cgroup v2 io.weight 调整块设备相对优先级
上一篇
cgroup v2 io.weight 调整块设备相对优先级
net.Resolver StrictErrors 处理部分解析结果
下一篇
net.Resolver StrictErrors 处理部分解析结果
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    408次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    484次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    493次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    438次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    266次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码