浏览器对 background-color 的 transition 始终在 sRGB 空间插值,即使使用 lch() 输入也无法触发 LCH 插值;真正实现 LCH 均匀过渡需结合 color-mix(in lch)、@property 控制变量及 linear-gradient(in lch) 渐变位移三者协同。

直接用 lch() 写过渡色,浏览器大概率会走 sRGB 插值——你写的 lch(60% 80 240) 到 lch(40% 80 240) 看起来是只调了 L,但实际动画过程仍发灰、断层、偏紫。这不是你写错了,是 CSS 动画机制本身的限制。
为什么 transition: background-color 不尊重 LCH?
浏览器对 background-color 的动画插值,底层始终在 sRGB 空间做线性混合,不管你的原始值是 rgb()、hsl() 还是 lch()。lch() 只是输入语法,不是运行时色彩空间。Chrome 117+ 虽支持 lch() 解析,但 transition 不触发 LCH 插值逻辑。
- 验证方式:写
background-color: lch(50% 90 240); transition: background-color 1s linear;,再用 DevTools 颜色拾取器拖动时间轴,观察中间帧 RGB 值是否呈线性变化(是)→ 说明没走 LCH -
@supports (color: lch(0 0 0))只能判断解析能力,不能保证插值空间 - Firefox 120 甚至对
lch()渐变都 fallback 到 sRGB 中间采样,更别说动画
真正能控制 LCH 插值的只有 color-mix() 和自定义属性
想让过渡“感知均匀”,必须绕过 transition 对颜色属性的直接接管,改用可被浏览器识别为“可动画”的中间变量。
- 把起止色用 JS 或构建脚本在 LCH 空间插值生成 3–5 个中间色,转成
rgb()或hsl(),存为 CSS 自定义属性:--bg-0: rgb(120, 150, 220); --bg-1: rgb(95, 130, 210); --bg-2: rgb(70, 110, 200); - 用
@property声明可动画的数字变量:@property --t { syntax: '<number>'; inherits: false; initial-value: 0; }</number> - 配合
color-mix(in lch, ...)实时混合:background-color: color-mix(in lch, var(--start) calc(100% - var(--t) * 1%), var(--end) calc(var(--t) * 1%)); - 再用
transition: --t 0.4s ease-out;控制变量变化——这才是唯一能真正走 LCH 插值的路径
linear-gradient(in lch, ...) 是唯一原生支持 LCH 插值的场景
这是目前唯一被 Chrome 112+ 正式支持、且插值过程真正在 LCH 空间完成的 CSS 特性。但它只适用于渐变,不适用于单色背景或边框动画。
立即学习“前端免费学习笔记(深入)”;
- 写法必须严格:
background-image: linear-gradient(in lch, lch(60% 70 240), lch(30% 70 240)); - 必须用
@supports检测:@supports (background: linear-gradient(in lch, red, blue)) { ... },否则旧版浏览器整个渐变失效(不是降级,是透明) - 别指望它和
transition联动:transition: background-image在大多数浏览器中无法平滑过渡两个不同in lch渐变,会卡顿或跳变 - 实用技巧:把渐变设为极大尺寸(
background-size: 400% 400%),再用background-position动画模拟“颜色流动”,此时transition: background-position是 GPU 加速且兼容性好
LCH 过渡的复杂点不在怎么写,而在怎么拆——你得把“颜色变化”这个直觉操作,拆解成“变量驱动 + 空间混合 + 渐变位移”三段逻辑。任何试图靠一行 transition: background-color 就实现 LCH 级平滑的做法,都会在 Safari 或 Firefox 上露出 sRGB 底色。这不是兼容性补丁能解决的,是渲染管线设计决定的硬边界。


















