CSS 滚动驱动动画:@scroll-timeline 实现视差效果

CLARA轻量论坛系统
CLARA轻量论坛系统 星耀SVIP管理员 黑卡会员
发布于 2026-09-17 15:17 ·2 浏览 ·0 回复

@scroll-timeline 这个 at-rule 已经被 CSS 规范废弃,现在实现视差效果的正确写法是用 `animation-timeline` 属性把动画挂到滚动时间线上,配合 `scroll()` 或 `view()` 两条内置时间线,几行 CSS 就能做出跟随滚动、不同速率的视差层,不需要一行 JS。

先搞清楚:@scroll-timeline 已经不能用了

结论:`@scroll-timeline` 是 Scroll-driven Animations 规范的早期草案语法,现已被删掉,改由 `scroll-timeline-name`、`scroll-timeline-axis`、`animation-timeline`、`timeline-scope` 这一组属性替代。你在老教程里看到的写法,在 Chrome/Edge 115+ 里直接不生效。

所以遇到"照着写没反应"的情况,第一件事是把 `@scroll-timeline` 整块删掉,换成属性写法。浏览器支持方面,Chromium 115+ 默认开启,Firefox 与 Safari 在 2025 年下半年陆续默认支持,写代码时用 `@supports (animation-timeline: scroll())` 包一层做渐进增强最稳妥。

视差的最小可用代码

结论:视差层的最短写法是 `animation-timeline: scroll(root block)`,不用给任何元素命名,直接把动画绑到页面根滚动条上;不同元素给不同的位移量,速率差就是视差。

/* 背景层:移动幅度大 */
.parallax-bg {
  animation: bg-move linear both;
  animation-timeline: scroll(root block);
}
@keyframes bg-move {
  from { transform: translateY(0) scale(1.1); }
  to   { transform: translateY(-25vh) scale(1.1); }
}

/* 前景层:移动幅度小,两层错开就产生了纵深 */
.parallax-fg {
  animation: fg-move linear both;
  animation-timeline: scroll(root block);
}
@keyframes fg-move {
  from { transform: translateY(0); }
  to   { transform: translateY(-8vh); }
}

三个要点:一是 `scroll(root block)` 里 `root` 指根滚动容器,`block` 指纵向轴,滚动容器不是根元素时写成 `scroll(nearest block)` 或直接写容器名;二是用滚动时间线时 `animation-duration` 会被忽略,进度完全由滚动位置决定;三是务必保留 `animation-fill-mode: both`,否则动画区间外元素会弹回初始位置。

用 view() 做"入场视差"更省事

结论:如果视差只发生在元素进入视口的阶段,用 `view()` 时间线比 `scroll()` 更省心,它自动以"元素与视口的交叉关系"为进度,还能用 `animation-range` 精确切区间。

.card {
  animation: rise linear both;
  animation-timeline: view();
  animation-range: entry 0% cover 45%;
}
@keyframes rise {
  from { opacity: 0; transform: translateY(60px) scale(.96); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

`animation-range` 支持的区间关键字有 `cover`、`contain`、`entry`、`exit`,`entry 0%` 表示元素刚碰到视口下沿,`cover 45%` 表示元素被视口覆盖到 45% 处。想做出"一边滚一边慢慢浮起"的视差感,把区间拉长(比如 `cover 0% cover 100%`)就行。`view()` 默认作用域是元素自身,不用额外声明。

两个必须避开的坑

结论:视差动画只能碰 `transform`、`opacity`、`filter` 这类合成属性,一旦动画 `top`、`margin`、`width`,滚动时会疯狂触发 layout,帧率直接崩。

第一个坑是时间线作用域。用命名时间线时,名字默认只在声明元素的子树里可见;如果动画元素和时间线元素是"兄弟关系",需要在共同祖先上加 `timeline-scope: --my-scroll;` 把它提升上去,否则 `animation-timeline: --my-scroll` 会静默失效。

第二个坑是无障碍。滚动驱动动画同样会前庭敏感人群不适,老老实实加一段:

@media (prefers-reduced-motion: reduce) {
  .parallax-bg, .parallax-fg { animation: none; }
}

降级方案别偷懒

结论:不支持的浏览器不会报错,只会停在动画第一帧,所以必须用 `@supports` 给一个静态可用的默认样式——比如背景层直接给 `transform: scale(1.1)` 做裁剪填充,而不是留一片空白。

.parallax-fg { /* 默认样式:可读、可用 */ }
@supports (animation-timeline: scroll()) {
  .parallax-fg { animation: fg-move linear both; animation-timeline: scroll(root block); }
}

如果项目必须覆盖老浏览器,再用 `IntersectionObserver` + `requestAnimationFrame` 走 JS 兜底,但记得只在 `@supports` 判定失败时才加载这段脚本,别让新浏览器白白多跑一份监听。

一句话收束:把 `@scroll-timeline` 换成 `animation-timeline: scroll()` 或 `view()`,只动画 transform、用 `animation-range` 控区间、用 `@supports` 兜底,视差效果十几行 CSS 就能落地,且比 JS 滚动监听更省电更顺滑。

本文转载自 Clara轻量论坛系统,原文地址:https://www.leleweb.cn/thread-437.html
转载请注明出处,版权归原作者所有。

全部回复 0

还没有回复,来抢沙发~