文章索引 
滚动驱动动画:让 scroll 监听器退休
很长一段时间里,「滚动到哪,动画走到哪」意味着 JS 监听 scroll 事件、算百分比、改样式。这条路径每一步都在主线程上,稍有不慎就是掉帧。CSS 滚动驱动动画把这件事整个搬进了浏览器内部。
核心概念只有一个
animation-timeline 允许把动画的时间轴从「时钟」换成「滚动位置」。动画本身还是普通的 @keyframes,变的只是驱动它的东西。
例子一:阅读进度条
本站文章页顶部的进度条没有一行 JS:
css
.reading-progress {
position: fixed;
inset: 0 0 auto 0;
height: 2px;
background: var(--accent);
transform-origin: 0 50%;
animation: grow linear both;
animation-timeline: scroll(root);
}
@keyframes grow {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
scroll(root) 表示时间轴来自根滚动容器。页面滚到底,动画正好播完。
例子二:元素入场
view() 时间轴更进一步:动画进度由「元素在视口中的位置」决定,相当于内置了 IntersectionObserver 加补间:
css
.card {
animation: rise linear both;
animation-timeline: view();
animation-range: entry 0% entry 80%;
}
@keyframes rise {
from {
opacity: 0;
transform: translateY(24px);
}
to {
opacity: 1;
transform: none;
}
}
animation-range 限定动画只在元素进入视口的前 80% 行程内播放,之后保持最终状态。
渐进增强与可访问性
截至写作时,Chromium 系浏览器与 Safari 都已支持,Firefox 仍需手动开启旗标。所以要包一层 @supports,让不支持的浏览器直接看到最终状态:
css
@supports not (animation-timeline: scroll()) {
.reading-progress { display: none; }
}
@media (prefers-reduced-motion: reduce) {
.card { animation: none; }
}
进度条属于状态指示,隐藏即可;入场动效属于装饰,在用户声明减少动态时应当彻底关闭。
什么时候还需要 JS
需要根据滚动做逻辑判断(比如目录高亮当前章节)时,IntersectionObserver 依然是正确工具。原则很简单:表现层交给 CSS 时间轴,语义层交给观察者。