纯 CSS 滚动动画完全指南
纯 CSS 滚动动画完全指南
滚动动画一直是前端开发中的热门话题。过去我们习惯用 JavaScript 监听 scroll 事件或引入 GSAP、ScrollMagic 等库来实现,但随着 CSS 的演进,现在仅用纯 CSS 就能实现相当丰富的滚动动画效果。本文将从基础到进阶,系统介绍几种纯 CSS 实现滚动动画的方案。
一、为什么选择纯 CSS?
- 性能更好:浏览器可以对 CSS 动画做合成层优化,避免主线程阻塞。
- 零依赖:不需要引入任何 JS 库,包体积更小。
- 渐进增强:配合
@supports可以优雅降级。 - 代码简洁:声明式写法,可读性强。
当然纯 CSS 也有局限,比如无法做复杂的滚动进度联动计算。但对于大部分"进场动画"场景,它已经足够。
二、方案一:animation-timeline: view() —— 滚动驱动动画
这是现代 CSS 最强大的滚动动画方案,属于 Scroll-driven Animations 规范。核心思想是:让动画的进度由元素在视口中的可见程度决定,而不是由时间决定。
基本用法
.card {
animation: fade-up linear both;
animation-timeline: view();
animation-range: entry 0% cover 40%;
}
@keyframes fade-up {
from {
opacity: 0;
transform: translateY(60px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
关键点解析:
animation-timeline: view():动画时间轴绑定到元素自身的可见性。animation-range: entry 0% cover 40%:定义动画的触发区间。entry 0%表示元素刚开始进入视口,cover 40%表示元素覆盖视口 40% 时动画结束。both:让动画在区间外保持首尾状态。
animation-range 常用关键字
| 关键字 | 含义 |
|---|---|
entry |
元素从视口边缘进入的过程 |
exit |
元素从视口离开的过程 |
cover |
元素完全跨越视口的过程 |
contain |
元素完全处于视口内的过程 |
例如 animation-range: entry 25% cover 50%,表示动画从"元素进入视口 25%"开始,到"元素覆盖视口 50%"结束。
错落进场效果
给一组卡片设置不同的 animation-range 起点,就能做出瀑布式错落出现的效果:
.card:nth-child(1) { animation-range: entry 0% cover 30%; }
.card:nth-child(2) { animation-range: entry 10% cover 40%; }
.card:nth-child(3) { animation-range: entry 20% cover 50%; }
三、方案二:animation-timeline: scroll() —— 滚动进度动画
如果说 view() 是"元素驱动",那么 scroll() 就是"滚动条驱动"。它把动画进度绑定到某个滚动容器的滚动位置。
.progress-bar {
position: fixed;
top: 0;
left: 0;
height: 4px;
background: linear-gradient(90deg, #6a5acd, #00d2ff);
transform-origin: left;
animation: grow linear;
animation-timeline: scroll(root);
}
@keyframes grow {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
这段代码实现了一个顶部阅读进度条。scroll(root) 表示绑定到根滚动容器;也可以写 scroll(nearest) 绑定到最近的可滚动祖先。
还可以用 scroll(root block) 指定滚动轴(block 为垂直,inline 为水平),做横向滚动画廊的进度指示非常方便。
四、方案三:animation-timeline: --custom —— 命名滚动时间轴
当动画元素和滚动容器不是同一元素时,可以用 scroll-timeline-name 定义一个命名时间轴,再通过 animation-timeline 引用。
.scroller {
overflow-y: auto;
height: 300px;
scroll-timeline-name: --my-scroller;
scroll-timeline-axis: block;
}
.indicator {
animation: grow linear both;
animation-timeline: --my-scroller;
}
这样即使 .indicator 不在 .scroller 内部,也能跟随它的滚动进度播放动画,非常适合做"侧边进度指示器 + 内容区滚动"的布局。
五、兼容性与降级
Scroll-driven Animations 目前已在 Chromium 115+ 和 Safari 26 支持,Firefox 仍在推进中。稳妥做法是用 @supports 做降级:
@supports (animation-timeline: view()) {
.card {
animation: fade-up linear both;
animation-timeline: view();
animation-range: entry 0% cover 40%;
}
}
/* 不支持时保持可见,避免内容消失 */
.card {
opacity: 1;
}
关键原则:默认状态写成"最终可见",只有在支持滚动时间轴时才把初始态设为隐藏。否则不支持的浏览器里内容会永远不可见。
六、方案四:经典 :target / position: sticky 技巧
在 Scroll-driven Animations 普及之前,纯 CSS 还有两个实用技巧。
1. position: sticky 实现滚动吸附
粘性定位本身就是一个"滚动联动"效果。配合 top 值,可以做吸顶导航、分段滚动等:
.section-title {
position: sticky;
top: 0;
background: #fff;
z-index: 10;
}
2. :target 实现滚动锚点切换
利用 URL 锚点触发 :target,可以做纯 CSS 的"滚动到某节时高亮"效果:
section:target {
animation: highlight 0.6s ease;
}
不过这种方式依赖点击跳转,不是真正的滚动监听,适用场景有限。
七、实战小例:滚动渐显卡片列表
综合以上,一个完整可用的例子:
<div class="list">
<div class="card">卡片 1</div>
<div class="card">卡片 2</div>
<div class="card">卡片 3</div>
</div>
.list {
display: grid;
gap: 40px;
padding: 40vh 20px;
}
.card {
padding: 40px;
border-radius: 16px;
background: #f4f4ff;
box-shadow: 0 8px 24px rgba(0,0,0,.08);
}
@supports (animation-timeline: view()) {
.card {
opacity: 0;
transform: translateY(80px) scale(.96);
animation: reveal linear both;
animation-timeline: view();
animation-range: entry 10% cover 35%;
}
}
@keyframes reveal {
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
八、性能与注意事项
- 优先动画
transform和opacity,避免动画width、height、top等触发重排的属性。 animation-range的百分比是相对于"可见性范围",不是视口高度,初学时容易混淆,建议用 DevTools 的动画面板观察。- 给
animation-timeline配linear缓动,滚动时间轴下非线性缓动会显得别扭。 - 避免过度使用:每个元素都绑滚动动画会带来额外合成开销,列表很长时建议只对首屏附近元素启用。
九、总结
纯 CSS 滚动动画如今已经足够成熟:
view()做元素进场渐显,是最常用、最简单的场景;scroll()做全局滚动进度条、视差;- 命名时间轴 打通跨元素联动;
@supports降级 保证旧浏览器可用。
掌握这几套组合拳,你就能在不写一行 JavaScript 的前提下,做出流畅、优雅的滚动动画。随着浏览器支持度持续提升,scroll-driven animations 很可能会成为未来滚动交互的标准做法,值得尽早掌握。
- 点赞
- 收藏
- 关注作者
评论(0)