HEX转RGB可无损,转HSL必有精度损失;HEX→RGB需归一化长度、校验字符、扩展简写并用parseInt(x,16)转换;RGB→HEX须padStart(2,"0")补零且clamp值域;HEX→HSL必须经RGB中转并归一化至[0,1]。

HEX 转 RGB 可以完全无损,转 HSL 则必然有精度损失 —— 因为 HSL 是非线性色度空间,浏览器内部也需经浮点计算,无法保证 round-trip 精确还原。
HEX → RGB:必须归一化长度并校验字符
常见错误是直接 substr(1,2) 切字符串,结果遇到 #abc 或带 alpha 的 #rrggbbaa 就崩溃。正确做法分三步:
- 先用正则
/^#?([a-f\d]{3}|[a-f\d]{6}|[a-f\d]{8})$/i匹配并排除非法输入 - 对
#abc类简写,每位扩展一次(a → aa),不是拼接也不是重复字符串,而是逐位复制 - 用
parseInt(x, 16)转十进制,必须传入基数 16,否则parseInt("ff")会按 10 进制解析成 NaN
RGB → HEX:补零不能靠 toString(16)
r.toString(16) 对 r = 10 返回 "a",但 CSS 要求两位十六进制,得是 "0a"。错误示例:#f00 实际是 rgb(255, 0, 0),但若没补零,rgb(10, 0, 0) 会变成 #a00(非法)。
- 每个通道必须用
(r).toString(16).padStart(2, "0") - 输入值超出 [0, 255] 时要 clamp:
Math.max(0, Math.min(255, r)) - 简写判断仅当
r % 17 === 0 && g % 17 === 0 && b % 17 === 0(即 0x00、0x11…0xff)才可缩为三位
HEX → HSL:必须经过 RGB 中转,且归一化不可省略
很多人试图跳过 RGB 直接算 HSL,数学上不成立。HSL 的亮度 L 和饱和度 S 公式依赖归一化后的 r, g, b ∈ [0, 1],如果直接拿 [0, 255] 区间值代入,max + min 分母可能超限,导致 S 为负数或 NaN。
立即学习“前端免费学习笔记(深入)”;
- 第一步:严格走
hexToRgb → rgbToHsl两步 - 第二步:
rgbToHsl输入必须是[0, 1],不是[0, 255] - 第三步:色相
H输出单位是度(0–360),中间计算结果要 ×60 并Math.round(),否则小数点后过长影响可读性
浏览器取色器显示的 HSL 值为什么和你手算不一致?
Chrome DevTools 取色器里长按色块切换到 hsl() 显示的值,是渲染管线最终输出的近似值,它基于当前像素实际采样(含抗锯齿、filter、混合等),不是原始 CSS 定义值。比如你写的是 hsl(30, 100%, 80%),取色器读出来可能是 hsl(30, 99%, 79%)。
- 设计稿标注了
#ffcc99,就该优先用这个值,别靠 HSL 反推 -
rgba()含透明度时,取色器显示的是叠加到默认背景(通常是白色)后的结果,rgba(0,0,0,0.2)在白底上≈#cccccc,但换黑底就完全不同 - 渐变、阴影、SVG
fill等非纯色场景,取色器读出的永远是合成后颜色,不是源码
真正容易被忽略的点是:HSL 的“无损”只存在于理论公式中;实际 JS 计算涉及多次浮点除法与三角函数,hslToRgb → rgbToHsl 循环两次,H 值大概率偏移 ±0.5°,S/L 也会有 0.1%~0.3% 误差。如果你在做颜色匹配或算法比对,别信单次转换结果,得用原始 HEX 或 RGB 作基准。


















