前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库CSS scroll-timeline 滚动驱动动画
CSS动画与视觉

怎么用纯 CSS 让动画跟着滚动条走,而不是按时间播放?

给元素写好 @keyframes,然后把 animation-timeline 设成 scroll() 或 view(),动画进度就不再由时间驱动,而是跟着滚动位置走。滚到顶是 0%,滚到底是 100%,中间按比例插值。

前端进阶之旅 · 一题精讲更新于 2026.09.29
CSS#动画与视觉#动画
先看核心答案读代码示例
理解线索

两种滚动时间线

  1. scroll()绑定滚动容器的整体滚动进度,从顶部 0% 到底部 100%
  2. view()绑定元素自身在视口中的可见比例,进入时 0%,完全可见时 100%
  3. animation-timeline替换默认的时间驱动,让动画进度由滚动位置决定

滚动驱动动画不触发 animationend 事件,因为动画没有「播完」的概念,用户随时可以往回滚。

核心回答

先记住这个答案

以前做滚动进度条、视差效果要监听 scroll 事件再用 JS 算比例,现在可以用纯 CSS 搞定。给元素写好 @keyframes,把 animation-timeline 设成 scroll(),动画进度就和滚动容器的滚动位置同步。不需要 JS,不会掉帧,因为浏览器在合成线程就能完成计算。

  • scroll() 跟整个滚动容器走
  • view() 跟元素进出视口走
  • 必须去掉 animation-duration 的时间值
  • 不支持的浏览器直接无动画,不会报错

用 scroll() 做阅读进度条

最常见的场景是页面顶部的阅读进度条。用户滚动页面时,一根横条从左到右慢慢填满。

进度条跟着页面滚动走CSS
/* 进度条从宽度 0 到 100% */
@keyframes grow {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

.progress-bar {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 3px;
  background: #3b82f6;
  transform-origin: left;
  animation: grow linear both;         /* 不写具体秒数 */
  animation-timeline: scroll(root);    /* 绑定根滚动容器 */
}

这里有两个关键点。第一,animation-timeline: scroll(root) 把动画绑到了整个页面的滚动容器上,root 就是 <html> 的滚动容器。第二,animation 缩写里不要写具体的秒数,比如 1s,否则浏览器会把它当成普通的时间驱动动画。用 linear 是因为滚动位置本身已经是线性的,再加缓动函数反而不直观。 scroll() 还能接受 nearest(最近的可滚动祖先)、self(元素自身)这些值,以及 block 或 inline 来指定方向。

用 view() 做元素入场动画

scroll() 绑的是整个容器的滚动进度,view() 绑的是某个元素在视口里的可见程度。元素刚进入视口时是 0%,完全可见时是 100%,适合做淡入、滑入这类入场动画。

元素滚进视口时渐显CSS
@keyframes fade-in {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: translateY(0); }
}

.card {
  animation: fade-in linear both;
  animation-timeline: view();
  animation-range: entry 0% entry 100%;
}

animation-range 控制动画在哪个阶段播放。entry 0% 是元素底边刚碰到视口底边,entry 100% 是元素完全进入视口。这样动画只在元素进入的过程中播放,滚出去时不会倒放。如果想让它滚出去时也有动画,可以加上 exit 阶段。

兼容性和降级方案

截至 2025 年底,Chrome 115+、Edge 115+ 和 Firefox 110+ 支持 animation-timeline,Safari 尚未支持。好消息是不支持的浏览器只是不执行动画,不会影响布局,所以可以做渐进增强:能跑就跑,不能跑就静态展示。

如果一定要在 Safari 上有动画效果,可以用 @supports 检测是否支持,不支持时走降级方案。

用 @supports 做渐进增强CSS
@supports not (animation-timeline: scroll()) {
  .progress-bar {
    /* Safari 降级:直接显示静态条或改用 JS */
    display: none;
  }
}

这样支持的浏览器正常显示进度条,不支持的浏览器隐藏这个元素,不影响页面功能。以前的 scroll-timeline-name 和 scroll-timeline-axis 这套命名写法在新版规范里被简化了,现在推荐直接用 scroll() 和 view() 函数,老写法有些浏览器已经废弃。

回答前,多想一步

容易答错的地方

以为要配合 JS 的 IntersectionObserver
滚动驱动动画是纯 CSS 方案,浏览器在合成线程处理,完全不需要 JS 参与。IntersectionObserver 适合做「进入视口时加 class」这类逻辑判断,但如果只是让动画跟滚动走,多此一举。
写了具体的 animation-duration 秒数
比如 animation: fade-in 0.5s linear both,这里的 0.5s 会让浏览器忽略 animation-timeline,退回时间驱动。正确写法是省略秒数或写 auto。
搞混 scroll() 和 view() 的用途
scroll() 跟的是容器的整体滚动进度,适合进度条、视差背景这类全局效果。view() 跟的是元素自身在视口中的位置,适合入场、退场动画。用反了虽然不报错,但动画时机和预期完全对不上。
试着用自己的话回答

面试官还会怎么问?

滚动驱动动画和 JS 的 scroll 事件监听相比有什么优势?

滚动驱动动画跑在合成线程,不阻塞主线程,不会因为 JS 执行慢而掉帧。scroll 事件在主线程触发,频率高时容易卡顿,还需要手动做 requestAnimationFrame 节流。纯 CSS 方案代码量也少得多。

animation-range 能精确到像素吗?

可以。animation-range 支持长度值,比如 entry 50px 表示元素底边进入视口 50px 时开始。也可以混用百分比和像素,比如 entry 0% entry 200px。

滚动驱动动画能暂停吗?

能,把 animation-play-state 设成 paused 就行。不过实际意义不大,因为用户停止滚动时动画自然就停在当前帧了。更常见的做法是用 animation-range 限定动画只在某个滚动区间内生效。

从一道题,走向一组知识

把知识连起来

动画与视觉

backdrop-filter 和 filter 有什么区别?毛玻璃效果怎么实现?

backdrop-filter 也会触发合成层,和滚动驱动动画一样需要关注 GPU 内存开销。

动画与视觉

做 3D 卡片翻转效果时,backface-visibility 解决的是什么问题?

卡片翻转动画如果想跟滚动联动,需要同时处理 backface-visibility 和滚动时间线。

参考资料

  • CSS:动画与视觉参考资料

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 用 scroll() 做阅读进度条
  3. 用 view() 做元素入场动画
  4. 兼容性和降级方案
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑