CSS Scroll-Driven Animations 来了:两行代码搞定滚动动画
滚动联动动画,以前你是怎么写的?
监听 scroll 事件 → 计算滚动百分比 → 手动改 style……每次都要写一堆胶水代码,性能还不稳定,页面卡顿全靠缘分。
css Scroll-Driven Animations 正式登场。Chrome 115+、Edge 115+ 已全面支持,今天就能用,两行 CSS 顶过你写的一整个 scroll 工具函数。
01 最直观的效果:阅读进度条
页面顶部那根随着滚动变长的进度条,用 JS 实现至少 20 行,CSS 只需要这些:
<div class="progress-bar"></div>/* CSS:滚动驱动,自动跑 */
.progress-bar {
position: fixed;
top: 0;
left: 0;
height: 4px;
background: #07C160;
transform-origin: left;
/* 关键两行 */
animation: grow linear;
animation-timeline: scroll(root); /* 监听根元素滚动 */
}
@keyframes grow {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}animation-timeline: scroll(root) 就是核心——把动画的播放进度和页面滚动位置绑定,滚多少走多少,全在 GPU 合成层运行,不阻塞主线程。
02 元素入场动画:滚到哪亮到哪
以前靠 IntersectionObserver + JS 切 class,现在纯 CSS 搞定:
/* 给每个卡片设置入场动画 */
.card {
animation: fade-in linear both;
animation-timeline: view(); /* 监听元素进入视口 */
animation-range: entry 0% entry 40%; /* 进入视口 0%~40% 时执行 */
}
@keyframes fade-in {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}view() 表示“监听该元素在视口中的可见范围”,animation-range 精确控制动画在哪个阶段触发,不需要写一行 JS。
03 视差滚动:图片比内容滚得慢
背景图滚动速度 ≠ 内容滚动速度,经典视差效果:
.hero-image {
animation: parallax linear;
animation-timeline: scroll(root);
}
@keyframes parallax {
from { transform: translateY(0px); }
to { transform: translateY(-120px); } /* 滚完整页移动 120px */
}
/* 文字层正常速度 */
.hero-text {
animation: none; /* 不绑定,随页面正常滚动 */
}以前这个效果要靠 background-attachment: fixed(移动端有兼容问题),或者写 JS 计算,现在用滚动时间线直接拿捏。
04 横向滚动画廊
内容区横向滚,侧边指示器跟着走:
/* 横向滚动容器 */
.gallery {
overflow-x: scroll;
scroll-snap-type: x mandatory;
display: flex;
}
/* 指示器跟随横向进度 */
.indicator {
animation: slide linear;
animation-timeline: scroll(nearest inline); /* inline = 横向 */
}
@keyframes slide {
from { transform: translateX(0); }
to { transform: translateX(calc(100% - 20px)); }
}scroll(nearest inline) 中:
nearest = 最近的滚动祖先
inline = 沿行内轴(即横向)监听
05 避坑指南
坑1:Safari 暂不支持
Safari 目前对 animation-timeline 支持不完整,生产环境记得加降级:
/* 降级:不支持时保持静止,不影响阅读 */
@supports not (animation-timeline: scroll()) {
.progress-bar { display: none; }
.card { opacity: 1; transform: none; }
}坑2:animation-fill-mode 别忘了写 both
入场动画不加 both,元素在动画开始前会闪一下默认状态:
/* ❌ 会闪 */
animation: fade-in linear;
/* ✅ 加 both,保持首尾状态 */
animation: fade-in linear both;坑3:view() 默认范围很大
不加 animation-range,动画从元素底部进入视口开始,到元素顶部离开视口结束,跨度太大动画会很慢。一定要手动指定范围:
animation-range: entry 0% entry 50%; /* 只在进入阶段的前半段播放 */兼容性一览
| 特性 | Chrome | Edge | Firefox | Safari |
|---|---|---|---|---|
| scroll() 时间线 | ✅ 115+ | ✅ 115+ | ✅ 110+ | ❌ 暂不支持 |
| view() 时间线 | ✅ 115+ | ✅ 115+ | ✅ 114+ | ❌ 暂不支持 |
PC 端用户占比高的产品可以放心用,移动端 iOS 用户注意加降级。
今日总结
| 场景 | 传统做法 | 新做法 |
|---|---|---|
| 阅读进度条 | JS 监听 scroll + 计算百分比 | animation-timeline: scroll(root) |
| 元素入场动画 | IntersectionObserver + JS 切 class | animation-timeline: view() |
| 视差滚动 | JS 计算偏移或 background-attachment | scroll(root) 绑定 transform |
| 横向指示器 | JS 监听 scrollLeft | scroll(nearest inline) |
用 animation-timeline 绑定滚动进度,让动画与滚动同步,零 JS、高性能、丝般顺滑。
本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!