原生 CSS 滚动进度驱动动画仅 Chrome 115+、Edge 115+、Opera 101+ 支持,Safari 和 Firefox 当前不支持 scroll-timeline;使用 @scroll-timeline 必须显式声明 source 和 axis,动态插入容器无效;animation-timeline 需与 animation-range 配对使用,animation-duration 值不影响实际节奏;衔接动画需 @keyframes 连续变化且避免混用未初始化属性;兼容性兜底必须用 JavaScript 检测并配合 Intersection Observer,@supports 会误报;时间线长度受盒模型等隐式因素影响,需真机验证。

原生 CSS 能实现滚动进度驱动的衔接动画,但仅限 Chrome 115+、Edge 115+、Opera 101+;Safari 和 Firefox 当前(2026年6月)仍不支持 scroll-timeline,强行使用会完全静默失效。
用 @scroll-timeline 定义时间轴时必须显式声明 source 和 axis
很多人写完 @scroll-timeline 规则却没效果,根本原因是:自定义滚动容器必须同时指定 source(滚动元素)和 axis(滚动方向),缺一不可。视口滚动(scroll(root))可省略 axis,但容器内滚动不行。
-
source: selector("#scroll-container")中的#scroll-container必须在 DOM 加载完成时已存在;动态插入的容器不会被自动识别 -
scroll-timeline-axis: block表示按垂直滚动距离计算(对应scrollTop),inline对应水平滚动(scrollLeft) - 若容器
overflow-y: hidden或内容未溢出,时间线长度为 0,动画永远不播放
animation-timeline 和 animation-range 必须配对使用
animation-timeline 单独存在毫无作用——它只是“绑定”,真正控制播放区间的是 animation-range。常见错误是只写前者,结果动画从滚动一开始就在播,或压根不动。
-
animation-range: entry-crossing 0% cover 100%表示:元素顶部刚接触视口顶部时开始,到元素完全覆盖整个视口高度时结束 - 想让动画在元素中心抵达视口中心时触发?得写
entry 50% exit 50%,不是0% 100% -
animation-duration: 1s是强制要求,但值本身不影响节奏;真实时长由滚动距离决定,设成0.001s也行
衔接动画的关键:用 @keyframes 控制属性连续变化,而非离散状态
所谓“衔接”,是指动画随滚动平滑过渡,不是“到点突变”。这就要求 @keyframes 中定义的属性变化是线性或缓动连续的,且起止值要能映射滚动全程。
立即学习“前端免费学习笔记(深入)”;
- 避免在同一个
@keyframes里混用opacity和transform却不设初始opacity: 0—— 元素会默认可见,造成“先闪一下再动” - 位移类动画推荐用
transform: translateY(),不要用top或margin-top,否则触发重排 - 若需多段衔接(比如先缩放再旋转),拆成多个独立元素分别绑定不同
animation-range,比在一个动画里硬塞所有帧更可控
兼容性兜底必须用 JavaScript + Intersection Observer
别指望加个 @supports 就能安全降级:@supports (animation-timeline: --tl) 在 Safari 中返回 true(误报),实际不执行。真要兼容,得靠 JS 检测运行时支持。
- 检测方式:创建一个临时元素,设置
style.animationTimeline = "scroll(root)",再读回值是否仍为"scroll(root)" - 降级方案优先用
IntersectionObserver,而不是window.onscroll:后者在快速滚动时易漏帧,且需手动节流 -
rootMargin: "0px 0px -30% 0px"是常用值,让动画提前启动,避免“刚露头就播完”的割裂感
滚动驱动动画最易被忽略的点是:时间线长度依赖于滚动容器的实际可滚动距离,而这个距离受盒模型、边距、字体渲染差异等隐式因素影响——同一段 CSS 在不同设备上可能触发位置偏移几十像素。上线前务必在真实机型上用 getBoundingClientRect() 打印数值验证。


















