唯一可靠路径是animation-timeline: scroll()和view(),但scroll()需满足滚动源、非直系子元素、明确定义0%/100%三条件;view()仅支持入场/退出类动画,不提供连续滚动进度;IntersectionObserver仍是主力方案,因原生CSS无法完成滚动位置到动画状态的映射计算。

目前能直接用原生 CSS 实现滚动驱动动画的唯一可靠路径,是 animation-timeline: scroll() 和 animation-timeline: view(),但必须接受 Chromium 115+ 的现实限制 —— Firefox 和 Safari 完全不识别,生产环境不能只靠它。
animation-timeline: scroll() 在 Chrome 中为何不生效
不是写错属性名,而是三个硬性条件缺一不可:
-
scroll()必须有明确的滚动源:比如scroll(root)(整页滚动)或scroll(nearest)(就近找可滚动祖先),拼成scroll(nearest-block)或漏括号会静默失败 - 目标元素不能是滚动容器的直系子元素 —— 比如父级设了
overflow-y: scroll,那动画元素得隔一层 wrapper,否则动画不触发 -
@keyframes必须明确定义0%和100%,只写from/to会被忽略
示例中常见错误:.box { animation-timeline: scroll(); } —— 这里 scroll() 没参数,Chrome 直接当无效值处理,动画退化为普通时间轴。
animation-timeline: view() 的实际边界在哪
它比 scroll() 兼容稍好(Safari 17.4+、Chrome 115+、Firefox 127+ 支持),但行为更“离散”:
-
view()只响应元素进入/离开视口,不提供连续滚动百分比,想做进度条或视差位移得搭配animation-range -
animation-range: entry 20% cover 50%表示动画从元素顶部距视口底边 20% 时开始,到顶部距视口顶边 50% 时结束 —— 这个计算基于元素自身尺寸和视口,不是滚动距离 - 元素若被
transform: scale(0.99)或opacity: 0.99影响,可能被判定为“不可见”,导致view()不触发
所以它适合入场/退出类动画,不适合需要线性映射的场景(比如随滚动缩放背景图)。
为什么 IntersectionObserver 仍是主力方案
因为 view() 不给你滚动位置,scroll() 浏览器支持太窄,而 IntersectionObserver 能手动算出元素在滚动容器内的相对偏移:
- 监听目标元素与滚动容器的
getBoundingClientRect()差值,再除以容器高度,就能拿到 0–1 的滚动进度 - 这个值可以绑定到
scale、transform、opacity等任意 CSS 自定义属性上,再用calc()驱动变化 - 第三方库如
locomotive-scroll就是封装了这套逻辑,并补全了 iOS 滚动抖动、被动监听等细节
真正难的不是监听滚动,而是把“滚动位置”映射成“动画状态”的中间计算 —— 这部分 CSS 原生没给,得自己写。
scroll() 和 view() 的命名时间线还能用吗
不能。所有带 @scroll-timeline 的写法(比如 @scroll-timeline slide-timeline { source: scroll(root); })已在 2024 年被规范正式废弃,现代浏览器已移除支持。现在只有 animation-timeline 属性 + 函数调用这一种入口。
如果你在旧代码里看到 scroll-timeline,要么删掉,要么重写为 animation-timeline: scroll(root) —— 但要注意,后者不接受命名引用,只能内联写函数。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











