scroll-timeline 是已被废弃的 CSS 实验性提案,现标准方案是 animation-timeline: scroll(),但仅 Chromium 115+ 支持;实际项目需用 IntersectionObserver 或第三方库模拟,且需注意滚动容器、关键帧定义和元素嵌套层级等硬性条件。

scroll-timeline 是什么,为什么不能直接用
它不是个函数或属性,而是 CSS 中一个被废弃的实验性规范提案。现在标准方案是 scroll-timeline 的替代品 —— 用 @keyframes 配合 animation-timeline: scroll() 实现滚动驱动动画,但目前仅 Chromium 115+ 原生支持,Firefox 和 Safari 完全不识别 scroll() 函数。
所以实际项目里,你得靠 IntersectionObserver 或第三方库(比如 locomotive-scroll)模拟,或者降级为视口内触发的简单淡入/缩放。
Chrome 中用 animation-timeline: scroll() 的最小可行写法
必须满足三个硬性条件,缺一不可,否则动画根本不触发:
- 目标元素需有明确的滚动容器(比如设置了
overflow-y: scroll的父级),且该容器是scroll-timeline的source -
@keyframes必须定义0%和100%关键帧,不能只写from/to - 动画元素本身不能是滚动容器的子元素直系后代(即不能“贴着滚”),建议中间至少隔一层包装容器
示例:
立即学习“前端免费学习笔记(深入)”;
.scroller {
overflow-y: scroll;
height: 400px;
}
.animated-box {
animation-name: fadeSlide;
animation-duration: 1s;
animation-timeline: scroll(block nearest);
}
@keyframes fadeSlide {
0% { opacity: 0; transform: translateY(20px); }
100% { opacity: 1; transform: translateY(0); }
}注意:scroll(block nearest) 中的 block 指滚动方向,nearest 表示就近匹配最近的可滚动祖先 —— 这个参数容易拼错成 nearest-block 或漏掉括号,导致整个动画静默失效。
用 IntersectionObserver 替代时的关键控制点
这是当前兼容性最稳的做法,但要注意:它只告诉你“进/出视口”,没法精确映射到滚动百分比。想实现类似 scroll-timeline 的连续变化,得手动计算元素在滚动容器内的相对位置。
常见坑:
-
IntersectionObserver的root不设默认值时,以视口为根;要绑定到某个容器,必须显式传入该容器 DOM 节点,且该容器需有overflow样式 -
entry.intersectionRatio是进入比例(0–1),但只在元素部分可见时有效;完全进入后恒为 1,无法反映“继续滚动时的位置变化” - 若想做滚动进度条或透明度随滚动线性变化,得用
entry.boundingClientRect.top和rootBounds.height手动算归一化值
简化的计算逻辑:
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
const progress = 1 - (entry.boundingClientRect.top - entry.rootBounds.top) / entry.rootBounds.height;
element.style.opacity = Math.max(0, Math.min(1, progress));
});
}, { root: document.querySelector('.scroller') });性能和回流风险在哪
滚动事件高频触发,但 IntersectionObserver 本身是异步回调,相对安全;真正危险的是在回调里读写布局属性(如 offsetHeight、getBoundingClientRect()),会强制同步回流。
避免方式:
- 所有读取操作(如
boundingClientRect)只在IntersectionObserver回调中做一次,不要在requestAnimationFrame里反复读 - 写样式优先用
transform和opacity,避开left/top/width等触发布局的属性 - 如果动画元素很多,别给每个都新建
IntersectionObserver实例,复用同一个实例监听多个目标
滚动驱动动画看着轻巧,但一不留神就变成主线程卡顿源 —— 尤其当元素带 box-shadow 或 backdrop-filter 时,GPU 加速未必兜得住。


















