色值转换

用 JavaScript 批量转换 HEX 到 RGB:校验、简写与透明度

实现一个支持三位、四位、六位和八位 HEX 的解析函数,保留无效输入的原始位置,并输出可用于 CSS 的 RGB 或 RGBA。

转换前先约定输入格式

单个颜色可以直接粘贴到转换器中。处理设计 Token 导出时,则需要让每一行都遵循同一套规则,并明确无效数据怎么处理。本文函数接受带或不带 # 的字符串,长度必须为 3、4、6 或 8 个十六进制字符;转换前去掉首尾空格,并先扩展简写格式。

CSS 的透明度位于末尾:#RGBA 会扩展成 #RRGGBBAA,与某些平台使用的 #AARRGGBB 顺序不同。函数会拒绝颜色名称、rgb(...) 字符串、空值和非字符串输入,不会自行猜测其含义。

理解通道换算与透明度的精度

完整 RGB 中,每个通道占两个十六进制字符。前一位表示十六的倍数,后一位表示个位:3B 等于 3 × 16 + 11,即 59;82 等于 8 × 16 + 2,即 130;F6 等于 15 × 16 + 6,即 246。因此 #3B82F6 转换为 rgb(59, 130, 246) 时,颜色本身保持一致。

简写的展开方式是逐位复制,不能给整个字符串统一补零。例如 #0af 要展开成 #00aaff,一个十六进制数字 d 对应的八位通道值就是 17 × d。透明度也采用相同规则:#0008 展开为 #00000088,其透明度为 136 / 255,约等于 0.5333。

八位 alpha 只有 256 个可表示的值,末尾的 80 表示 128 / 255,约为 0.50196,并不精确等于一半。计算时使用返回的 a 数值;示例 CSS 为便于阅读保留四位小数。这个输出是近似文本,如果流程要求完整保留原始字节,应另行保存原值。

解析颜色通道并保留透明度

应先校验完整字符串,再调用 parseInt。例如 parseInt('ffzz00', 16) 可能返回已经解析的部分数值,而不是报告整个输入无效。使用完整匹配的正则表达式,可以避免错误 Token 被静默接受。

RGB 通道保留为 0–255 的整数,透明度由最后一个字节除以 255 得到。计算时保留透明度数值,只在生成 CSS 文本时舍入。遇到无效输入,函数返回 null,让批处理保留原始行并报告问题。

JavaScript
function hexToRgb(input) {
  if (typeof input !== 'string') return null;
  let hex = input.trim().replace(/^#/, '');
  if (!/^(?:[0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i.test(hex)) {
    return null;
  }
  if (hex.length === 3 || hex.length === 4) {
    hex = [...hex].map((digit) => digit + digit).join('');
  }
  const r = parseInt(hex.slice(0, 2), 16);
  const g = parseInt(hex.slice(2, 4), 16);
  const b = parseInt(hex.slice(4, 6), 16);
  const hasAlpha = hex.length === 8;
  const a = hasAlpha ? parseInt(hex.slice(6, 8), 16) / 255 : 1;
  const alphaText = Number(a.toFixed(4));
  const css = hasAlpha
    ? 'rgba(' + [r, g, b, alphaText].join(', ') + ')'
    : 'rgb(' + [r, g, b].join(', ') + ')';
  return { r, g, b, a, css };
}

让错误与原始输入保持对应

将每一行映射成一个结果对象,不要直接过滤掉无效颜色。这样既能保持顺序,也方便检查错误,避免后续颜色错位写入其他设计 Token。

JavaScript
const inputs = ['#3b82f6', '#0af', '#0008', '#ff000080', 'oops'];
const results = inputs.map((input) => {
  const color = hexToRgb(input);
  return color
    ? { input, ok: true, css: color.css }
    : { input, ok: false, error: 'Invalid HEX color' };
});
console.table(results);
批量转换示例的预期输出
输入结果
#3b82f6rgb(59, 130, 246)
#0afrgb(0, 170, 255)
#0008rgba(0, 0, 0, 0.5333)
#ff000080rgba(255, 0, 0, 0.502)
oopsInvalid HEX color

导入 Token 文件之前检查边界输入

先检查通道两端:#000 应输出全零,#fff 应输出三个 255,大小写混合的 aBc 应输出 170、187、204。确认首尾空格是有意接受的格式,而中间空格、重复的 #、五位色值和非十六进制字符都应被拒绝,不能悄悄修复为另一个颜色。

透明度需要单独核对。#ff000000 是有效的全透明红色,既不是错误输入,也不是不透明红色;#0000 则是有效的全透明黑色。如果直接用 alpha 的真假判断是否存在透明度,就会错误处理零值,因此代码根据展开后的长度判断。调用 trim 前先检查类型,也能避免 null、数字或缺失字段导致异常。

如果检查往返转换,应比较规范化通道与原始 alpha 字节,而不是直接比较字符串。在本文函数中,#fff、#FFFFFF 和 FFFFFF 都代表同一不透明颜色,但经过舍入的 rgba 文本不适合作为无损存储格式。需要追溯时,应同时保存原始输入和规范化结果。

将转换规则接入实际数据流程

示例 map 处理的是已经结构化的数组。读取 CSV、JSON 或 Token 包属于另一个步骤,应使用对应格式的解析器,并保留 Token 标识、来源行号和原始值。不能简单按逗号切分整个 CSV,因为引号字段内也可能包含逗号和换行,它们与颜色语法无关。

提前决定无效行是阻止导出,还是返回给编辑者修正。设计 Token 构建通常适合一次报告所有错误,等修复后再生成产物。不要把解析失败默认替换成黑色,也不要直接丢弃那一行,否则输入问题可能被隐藏成一次不易察觉的界面变更。

对于现有数组,转换工作量随行数增长。本文示例同时保留输入与结果,适合小型导出;较大的导入可能需要分块或流式读取。让解析逻辑独立于 DOM 更新,同一套规则就能用于构建脚本、Worker 或浏览器界面,避免不同入口采用不同校验标准。

区分格式转换与视觉验证

不透明 sRGB 颜色从 HEX 转成 RGB,只会改变表达方式,不会改变颜色。这个函数不负责 Display P3 转换、ICC 色彩管理,也不能将屏幕颜色直接变成印刷可用的 CMYK 规格。

仅有透明度值还不能计算最终对比度。半透明前景色必须先与实际背景合成,不能去掉 alpha 后,就把剩余 RGB 当作显示结果。应在真实界面中检查最终组合。

下方 HEX 转 RGB 工具适合快速核对不透明颜色。本文代码额外支持带透明度的 HEX;站内单一任务转换器面向 #RGB 与 #RRGGBB 实色输入。

来源与延伸阅读

← 返回全部文章