写过前端的都踩过这个坑——滚动动画配了这么多年,今天才知道 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 是纯视觉的,有三类场景它替代不了:

  1. 懒加载图片——需要在元素可见时发网络请求,CSS 管不了
  2. 只触发一次的事件——IntersectionObserver 的 unobserve() 可以精确控制只触发一次,CSS 动画每次滚动都会重跑
  3. 跨浏览器的精确阈值——需要 25%、60%、85% 多个阈值分别触发不同逻辑,CSS 目前只支持 entry/cover/exit/contain 四个关键字的百分比,不支持多阈值

纯视觉的入场动画、进度条、视差效果——这些现在都应该用 CSS,不再需要任何 JS。

评论区

0 条评论

登录后可评论。