纯CSS实现滚动变色唯一可行路径是@scroll-timeline+animation-timeline配合hsl()或color-mix()间接驱动,因color/background-color不被SDA原生支持,仅opacity、transform等属性可直接动画。

纯 CSS 实现颜色随滚动位置变化,目前只有 @scroll-timeline + animation-timeline 这一条路可行;但必须接受它不支持 color、background-color 等属性的直接动画——得靠 hsl() 或 color-mix() 配合 CSS 变量间接驱动。
为什么不能直接用 animation 动画 color 属性
color 和 background-color 不在 CSS Scroll-driven Animations(SDA)当前支持的可动画属性列表中。Chrome 126+、Firefox 119+ 会静默忽略这类绑定,或报 Invalid property 错误。你看到的“变色生效”,大概率是浏览器 fallback 到 document timeline 导致的意外播放,而非真实滚动驱动。
- 真正被 SDA 原生支持的颜色相关属性只有:
opacity、transform、filter(仅部分函数)、clip-path - 想让颜色“动起来”,必须把颜色值拆解为可插值的数值维度:比如用
hsl(var(--h), 80%, 60%),只动画--h变量 - 或者用
color-mix(in srgb, #3b82f6 0%, #ef4444 100%)+color-mix(in srgb, #3b82f6 calc(100% - var(--p)), #ef4444 var(--p)),让--p被动画驱动
用 @scroll-timeline + hsl() 实现平滑滚动变色
核心是把滚动进度映射为 HSL 的 hue 值,再用 @keyframes 控制变量变化。整个流程不依赖 JS,但需注意时间线定义位置和命名一致性。
-
@scroll-timeline必须写在顶层作用域(不能嵌套在@media或选择器内),例如:@scroll-timeline scroll-progress { source: auto; orientation: "block"; start: 0%; end: 100%; } - 目标元素上设置:
animation: hue-shift linear; animation-timeline: scroll-progress; -
@keyframes中不写颜色,只改自定义属性:@keyframes hue-shift { from { --h: 210; } to { --h: 360; } } - CSS 中消费该变量:
color: hsl(var(--h), 80%, 50%); - 记得给
:root设默认值::root { --h: 210; },避免首次渲染无色
兼容性与 fallback 方案怎么写
截至 2026 年 5 月,@scroll-timeline 在 Chrome 115+、Edge 115+、Safari 17.4+ 原生支持;Firefox 需开启 layout.css.scroll-driven-animations.enabled 实验标志,且不支持 source: auto,得显式写 source: selector(html)。
立即学习“前端免费学习笔记(深入)”;
- 不要用
animation: name 1s—— 必须写animation: name auto,否则动画时长固定,和滚动脱节 - 为不支持的浏览器提供降级:
@supports not (animation-timeline: name) { color: #3b82f6; } - 如果页面有横向滚动或
overscroll-behavior: contain,source: auto可能失效,改用source: selector(#main-scroll-container)并确保该容器有明确高度和overflow-y: scroll - 避免在
@scroll-timeline中使用selector()动态定位触发元素——每次滚动都会触发样式查询,性能极差
真正难的不是写对那几行 CSS,而是理解:滚动驱动变色的本质不是“CSS 感知了滚动”,而是浏览器把滚动位置编译成一个标准化的时间轴信号,再喂给动画系统。一旦你试图用它驱动 layout 相关属性,或在动态容器上乱绑 timeline,性能就会立刻崩掉——这时候 JS + IntersectionObserver 反而是更稳的选择。


















