css transition 对颜色插值默认采用 rgb 空间线性计算,即 r、g、b 三通道各自独立按时间比例插值,不感知人眼视觉均匀性;如 #ff0000 → #0000ff 中途得 #800080,而 #ff0000 → #00ff00 会经过灰褐调的 rgb(128,128,0);目前无浏览器在 transition 中原生支持 lch/oklch 插值,仅 @keyframes(chrome 112+/safari 16.4+)在全用同色函数时才启用 lch 空间插值。

transition 怎么算颜色中间值
CSS transition 对颜色做插值时,**不是按 RGB 十六进制字符串字面量线性变化**,也不是直接在 HSL 圆环上走最短弧——它默认使用 RGB 空间线性插值,也就是对 R、G、B 三个通道各自独立做数值插值。
比如从 #ff0000(红)过渡到 #0000ff(蓝),浏览器会把两个颜色转成 RGB 值:rgb(255, 0, 0) → rgb(0, 0, 255),然后在每个通道上按时间比例计算中间值:
time = 0.5 → R: 255×0.5 + 0×0.5 = 127.5 ≈ 128<br>G: 0×0.5 + 0×0.5 = 0<br>B: 0×0.5 + 255×0.5 = 127.5 ≈ 128<br>→ rgb(128, 0, 128) → #800080(紫)
这个过程完全不考虑人眼感知或色相跳跃,所以会出现“经过脏棕色”这类意外中间色(比如 #ff0000 → #00ff00 中途会经过 rgb(128,128,0),即橄榄黄)。
想避免脏色?别依赖默认 RGB 插值
如果你发现过渡中出现难看的灰褐/棕黄调,大概率是 RGB 各通道异步升降导致的。解决思路只有两个:
立即学习“前端免费学习笔记(深入)”;
- 用
color-mix()(现代浏览器)显式指定插值空间,例如color-mix(in lch, red 50%, blue 50%) - 改用
@property+transition控制自定义属性,并在 CSS 中用lch()或oklch()写关键帧色值(需注册为color类型) - 退而求其次:手动预设几个关键中间色,用
@keyframes替代transition,绕过浏览器自动插值
注意:transition: color 300ms 永远走 RGB;transition: background-color 300ms 也一样——只要属性值是 color 类型,底层插值逻辑就固定。
浏览器是否支持 LCH/OKLCH 插值
目前(2024 年中)没有任何浏览器在 transition 中原生支持 LCH 或 OKLCH 空间插值。即使你写了:
div {<br> background-color: oklch(0.6 0.2 270);<br> transition: background-color 300ms;<br>}<br>div:hover {<br> background-color: oklch(0.6 0.2 0);<br>}
浏览器仍会先把两个 oklch() 值转成 RGB,再在 RGB 空间插值——oklch() 在这里只起“输入语法糖”作用,不改变插值行为。
真正生效的 LCH 插值,目前只存在于 @keyframes 动画中(Chrome 112+、Safari 16.4+ 支持),且必须所有关键帧都用同一种色彩函数(如全用 lch()),浏览器才会尝试在 LCH 空间插值。
实际调试建议:用 DevTools 实时看中间帧
不要靠猜。打开 Chrome / Edge 的 DevTools → Elements 面板 → 找到目标元素 → 勾选 :hover 或其他伪类 → 在 Styles 面板里点开 color / background-color 属性右侧的色块图标 → 拖动下方的过渡时间滑块,实时观察 RGB 值变化。
你会发现:R/G/B 三通道数值始终是平滑直线变化,没有缓动曲线影响——因为 easing 影响的是“时间进度”,不影响“颜色空间选择”。也就是说,ease-in-out 只让 0→0.5→1 的时间分布不均,但每个时刻的颜色值仍是 RGB 线性算出来的。
真正难处理的,是设计师要“视觉均匀过渡”,而浏览器只管数学均匀——这个 gap,目前还得靠预计算中间色、分段动画或 JS 控制来填。










