CSS 滚动驱动动画:零 JS 实现视差滚动、进度条和揭示效果

前端工程(更新于 2026年6月2日)

告别 scroll 事件监听

传统滚动动画依赖 JS 监听 scroll 事件:

// ❌ 传统方式:JS 监听 + 强制同步布局
window.addEventListener('scroll', () => {
  const progress = window.scrollY / (document.body.scrollHeight - window.innerHeight);
  progressBar.style.width = `${progress * 100}%`;
  // 每次 scroll 都触发重排!
});

CSS 滚动驱动动画让这一切用纯 CSS 完成,运行在合成线程,不阻塞主线程:

/* ✅ 纯 CSS:阅读进度条 */
.progress-bar {
  width: 100%;
  height: 3px;
  background: blue;
  transform-origin: left;
  animation: progress linear;
  animation-timeline: scroll();
}

@keyframes progress {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

scroll-timeline 语法

匿名滚动时间线

/* 基于最近可滚动祖先的滚动 */
.animated {
  animation-timeline: scroll();
}

/* 指定方向 */
.animated-x {
  animation-timeline: scroll(x); /* 水平滚动 */
}

.animated-y {
  animation-timeline: scroll(y); /* 垂直滚动 */
}

命名滚动时间线

/* 在滚动容器上声明时间线名称 */
.scroll-container {
  scroll-timeline: --page-scroll y;
  overflow-y: auto;
}

/* 在动画元素上引用 */
.animated-element {
  animation: fade-in linear;
  animation-timeline: --page-scroll;
}

view-timeline 语法

view-timeline 基于元素进入/离开视口的进度:

.reveal-item {
  view-timeline: --item-reveal;
  animation: reveal linear both;
  animation-timeline: --item-reveal;
}

@keyframes reveal {
  from {
    opacity: 0;
    transform: translateY(50px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

view-timeline-range:精确控制动画范围

.reveal-item {
  view-timeline: --item-reveal;
  animation: reveal linear both;
  animation-timeline: --item-reveal;
  /* 只在元素进入视口的前 30% 播放动画 */
  animation-range: entry 0% entry 30%;
}
range 值 含义
entry 元素开始进入视口
exit 元素开始离开视口
cover 元素完全在视口内
contain 元素从完全进入到开始离开
entry-crossing 元素正在穿越视口边缘进入

实战一:视差滚动

.parallax-container {
  overflow-y: auto;
  scroll-timeline: --parallax y;
  height: 100vh;
}

.parallax-bg {
  animation: parallax-move linear;
  animation-timeline: --parallax;
}

@keyframes parallax-move {
  from { transform: translateY(0); }
  to { transform: translateY(-200px); }
}

/* 不同深度的视差速度 */
.parallax-layer-1 { animation: parallax-move-slow linear; animation-timeline: --parallax; }
.parallax-layer-2 { animation: parallax-move-medium linear; animation-timeline: --parallax; }
.parallax-layer-3 { animation: parallax-move-fast linear; animation-timeline: --parallax; }

@keyframes parallax-move-slow { to { transform: translateY(-50px); } }
@keyframes parallax-move-medium { to { transform: translateY(-150px); } }
@keyframes parallax-move-fast { to { transform: translateY(-300px); } }

实战二:阅读进度条

/* 页面顶部的阅读进度指示器 */
.reading-progress {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 4px;
  background: linear-gradient(90deg, #3b82f6, #8b5cf6);
  transform-origin: left;
  z-index: 1000;
  animation: reading-progress linear;
  animation-timeline: scroll(root y);
}

@keyframes reading-progress {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

实战三:滚动揭示(Scroll Reveal)

/* 从下方淡入 */
.reveal-up {
  view-timeline: --reveal;
  animation: reveal-up linear both;
  animation-timeline: --reveal;
  animation-range: entry 0% entry 40%;
}

@keyframes reveal-up {
  from {
    opacity: 0;
    transform: translateY(80px) scale(0.95);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

/* 从左侧滑入 */
.reveal-left {
  view-timeline: --reveal;
  animation: reveal-left linear both;
  animation-timeline: --reveal;
  animation-range: entry 0% entry 30%;
}

@keyframes reveal-left {
  from {
    opacity: 0;
    transform: translateX(-60px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

交错延迟

/* 用 animation-delay 实现交错效果 */
.stagger-item:nth-child(1) { animation-delay: 0ms; }
.stagger-item:nth-child(2) { animation-delay: 80ms; }
.stagger-item:nth-child(3) { animation-delay: 160ms; }
.stagger-item:nth-child(4) { animation-delay: 240ms; }
.stagger-item:nth-child(5) { animation-delay: 320ms; }

实战四:水平滚动区域

.horizontal-scroll {
  overflow-x: auto;
  scroll-timeline: --hscroll x;
}

.card {
  animation: card-appear linear both;
  animation-timeline: --hscroll;
  animation-range: entry 0% entry 20%;
}

@keyframes card-appear {
  from {
    opacity: 0;
    transform: scale(0.9);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

实战五:粘性头部收缩

.page {
  scroll-timeline: --page y;
  overflow-y: auto;
}

.sticky-header {
  position: sticky;
  top: 0;
  animation: header-shrink linear both;
  animation-timeline: --page;
  animation-range: 0px 100px;
}

@keyframes header-shrink {
  from {
    padding: 24px;
    font-size: 2rem;
  }
  to {
    padding: 12px;
    font-size: 1.25rem;
  }
}

实战六:图片序列帧动画

.sprite-container {
  scroll-timeline: --sprite y;
  overflow-y: auto;
}

.sprite-image {
  animation: sprite-steps steps(24) both;
  animation-timeline: --sprite;
}

@keyframes sprite-steps {
  from { background-position: 0 0; }
  to { background-position: -2400px 0; } /* 24帧 × 100px宽 */
}

JS vs CSS 滚动动画对比

维度 JS (scroll 事件) CSS scroll-timeline
性能 主线程,可能卡顿 合成线程,60fps
代码量 20-50 行 5-10 行
兼容性 全部浏览器 Chrome 115+, Firefox 125+
灵活性 任意逻辑 线性/分段动画
调试 console.log DevTools Animations 面板
内存 需手动清理监听器 自动管理

Polyfill 策略

// 检测支持
const supportsScrollTimeline = CSS.supports('animation-timeline', 'scroll()');

if (!supportsScrollTimeline) {
  // 降级到 IntersectionObserver + CSS 类切换
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      entry.target.classList.toggle('in-view', entry.isIntersecting);
    });
  }, { threshold: 0.2 });

  document.querySelectorAll('.reveal-item').forEach(el => {
    observer.observe(el);
  });
}

浏览器兼容性

浏览器 scroll-timeline view-timeline animation-range
Chrome 115+
Edge 115+
Firefox 125+
Safari 17.4+

Safari 仍在实现中,建议使用特性检测 + IntersectionObserver 降级。


最佳实践

  1. 优先使用 view-timeline:大多数滚动揭示效果用 view-timelinescroll-timeline 更简洁
  2. animation-range 精确控制:避免动画在不可见时仍在运行
  3. will-change 配合:对动画元素添加 will-change: transform, opacity
  4. 减少同时动画元素:超过 20 个同时运行的滚动动画可能影响性能
  5. 特性检测降级:Safari 不支持时用 IntersectionObserver 降级

总结

CSS 滚动驱动动画是 2026 年前端最令人兴奋的新特性之一。它让视差滚动、阅读进度条、滚动揭示等经典效果从 JS 回归 CSS,性能提升显著(合成线程运行,不阻塞主线程)。虽然 Safari 支持尚不完善,但配合 IntersectionObserver 降级,现在就可以在生产环境使用。

本站提供浏览器本地工具,免注册即可试用 →

#滚动驱动动画#CSS动画#scroll-timeline#视差滚动#无JS动画