写过前端的都踩过这个坑——滚动动画配了这么多年,今天才知道 entry/cover/exit 从来没配对过——CSS animation-range 把这件事彻底说清楚了
写过前端的都踩过这个坑——写滚动动画要么靠 scroll 事件算进度,要么靠 IntersectionObserver 猜时机,animation-range 属性虽然用了但从来不确认配的是 entry 0% 还是 cover 0%,反正效果差不多就凑合了。实际上 entry 和 cover 控制的时间窗口差了十万八千里,配错了动画要么跑不完就消失了,要么等到元素都快滚出屏幕了才刚开始。
CSS scroll-driven animations 在 2026 年已经接近 90% 全球覆盖——Chrome 115(2023 年 7 月)、Safari 18(2025 年 9 月)、Firefox 126(2025 年 6 月)全部稳定。但 animation-range 这个最核心的精细控制能力,大部分人只知道皮毛。
animation-range 四个关键字到底管什么
先说清楚坐标系。view() 时间线映射的是「元素在视口中的位置」——0% 是元素即将从底部进入视口的瞬间,100% 是元素完全从顶部离开视口。这整段旅程,animation-range 给你四种切法:
entry:元素正在进入视口的过程中。从底部边缘接触视口底部开始,到元素顶部边缘接触视口顶部结束。对入场动画最友好。
cover:元素覆盖视口的全过程。从底部边缘接触视口底部开始,到顶部边缘离开视口顶部结束。时间窗口最长,也最容易被滥用。
exit:元素正在离开视口的过程中。从元素顶部边缘接触视口底部开始,到完全离开视口顶部结束。对退场动画有用。
contain:元素完全被视口包含的时间段。从完全进入(顶部接触视口顶部)到开始离开(底部接触视口底部)。时间窗口最短。
看代码更直观:
/* 入场动画:元素底部进入视口 → 顶部接触视口顶部 */
animation-range: entry 0% entry 100%;
/* 覆盖动画:元素底部进入 → 顶部离开 */
animation-range: cover 0% cover 100%;
/* 精确控制:元素进入 30% 后才开始跑,跑完前 40% 的视口旅程 */
animation-range: entry 30% entry 40%;
第三种是最容易被忽略的写法。默认 entry 0% entry 100%,但如果你想在元素已经部分可见时才开始动画(比如已经进入了 20% 才开始淡入),直接改前一个百分比就行。
三个实战场景
场景一:阅读进度条
@keyframes read-progress {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
.progress-bar {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 3px;
background: #4361ee;
transform-origin: left center;
animation: read-progress linear both;
animation-timeline: scroll(root block);
}
scroll(root block) 绑定到根文档的垂直滚动轴,不需要 animation-range。
场景二:入场动画——元素进入视口的前 30% 完成淡入
@keyframes reveal {
from { opacity: 0; transform: translateY(2rem); }
to { opacity: 1; transform: translateY(0); }
}
.section {
animation: reveal ease-out both;
animation-timeline: view();
animation-range: entry 0% entry 30%;
}
这里关键:animation-fill-mode: both 保证动画在范围外也保持最终状态。不会的元素会在滚动回顶部时闪回透明状态。
场景三:视差效果——元素在离开视口的过程中持续位移
@keyframes parallax-out {
to { transform: translateY(-4rem); opacity: 0; }
}
.hero-text {
animation: parallax-out linear both;
animation-timeline: view();
animation-range: exit 0% exit 100%;
}
entry 和 cover 的真实差距
很多人在入场动画里用 entry 0% entry 100%,但如果页面布局复杂、元素很大,实际效果和 cover 0% cover 100% 完全不同。
比如一个高度 1200px 的 Hero 区块,用 entry 时动画在 Hero 底部刚碰到视口底部时开始,在 Hero 顶部到达视口顶部时结束——这个时间窗口很短,动画可能跑得很快或者根本没跑完。用 cover 时间窗口就长得多,因为要等整个 Hero 完全滚出视口。
选 entry 还是 cover 的判断标准:
- 元素高度 < 视口高度 → entry 和 cover 差异不大,用 entry 更符合直觉
- 元素高度 > 视口高度 → 用 entry 可能动画跑不完,改用 entry 0% cover 50% 或直接用 cover
- 希望动画在整个可见期间持续 → 用 cover 加百分比范围
浏览器支持与渐进增强
2026 年 9 月:Chrome 115+、Edge 115+、Safari 18+、Firefox 126+ 全部稳定,全球覆盖约 90%。渐进增强写法:
.section {
opacity: 1;
transform: translateY(0);
}
@media (prefers-reduced-motion: no-preference) {
@supports (animation-timeline: view()) {
.section {
animation: reveal ease-out both;
animation-timeline: view();
animation-range: entry 0% entry 30%;
}
}
}
不支持的浏览器直接显示最终状态,不跑动画。
什么时候仍然需要 IntersectionObserver
CSS scroll-driven animations 是纯视觉的,有三类场景它替代不了:
- 懒加载图片——需要在元素可见时发网络请求,CSS 管不了
- 只触发一次的事件——IntersectionObserver 的 unobserve() 可以精确控制只触发一次,CSS 动画每次滚动都会重跑
- 跨浏览器的精确阈值——需要 25%、60%、85% 多个阈值分别触发不同逻辑,CSS 目前只支持 entry/cover/exit/contain 四个关键字的百分比,不支持多阈值
纯视觉的入场动画、进度条、视差效果——这些现在都应该用 CSS,不再需要任何 JS。
评论区
登录后可评论。