各种颜色类型及之间的转换

2018-03-28
Design
color

作为前端,写 css 时经常遇到颜色的设置。常见颜色格式有 CSS 命名颜色、rgb、hex、hsl,不常用的 hwb、cmyk等,还有一些不能在 css 里用的,比如 lab、xyz。这篇文章简单介绍一下各种颜色类型,以及它们之间的转换。

总览

先看看下文会提及的颜色类型的简单对比:

名称 示例 能否用于 CSS
rgb rgb(11, 22, 33)
hex #rgb / #rrggbb
hsl hsl(210, 100%, 60%)
CSS 命名颜色 white
hwb hwb(54, 57%, 5%) CSS 4 提案
cmyk cmyk(16, 25, 0, 8) CSS 4 提案
hsv hsv(114, 40%, 90%)
xyz xyz(20, 59, 42)
lab lab(81, -78, 72)

这里没有提及透明度,透明度应该是独立于颜色的,下面在介绍涉及 CSS 的颜色类型时会一并提及透明度的设置。

RGB

简介

RGB 即红绿蓝,也被称为三原色。RGB 颜色模型是一种 加色模型,将红绿蓝三种色按不同的比例相加,可以产生不同的色彩。显示器就是用此原理显示绚丽的图案。

红绿蓝

在 CSS 中使用

红绿蓝的比例,有多种表示方式。在 CSS 里面,可以为 0% ~ 100% 的百分比或者 0 ~ 255 的数字或者 16 进制,比如:

color: rgb(25, 25, 25);
color: rgb(10%, 10%, 10%);

CSS 还支持同时设置透明度,值为 0 ~ 1 的小数:

color: rgba(25, 25, 250.3);
color: rgba(10%, 10%, 10%, 0.3);

RGB 颜色模式好处在于容易理解、对计算机友好,但是颜色值对人类来说并不直观。

转换

rgb => hex:

function rgb2hex (r, g, b) {
  const addZero = str => (str.length < 2 ? `0${str}` : str);
  const red = addZero(Number(r).toString(16));
  const green = addZero(Number(g).toString(16));
  const blue = addZero(Number(b).toString(16));
  return `#${red}${green}${blue}`;
} 

rgb => hsl,参考

function rgb2hsl (r, g, b) {
  let h = 0;
  let s = 0;
  let l = 0;

  r = r / 255;
  g = g / 255;
  b = b / 255;

  const max = Math.max(r, g, b);
  const min = Math.min(r, g, b);
  const sum = max + min;
  const delta = max - min;

  if (max === min) h = 0;
  else if (max === r && g >= b) h = 60 * ((g - b) / delta);
  else if (max === r && g < b) h = 60 * ((g - b) / delta) + 360;
  else if (max === g) h = 60 * ((b - r) / delta) + 120;
  else if (max === b) h = 60 * ((r - g) / delta) + 240;

  l = sum / 2;

  if (l === 0 || max === min) s = 0;
  else if (0 < l && l <= 0.5) s = delta / sum;
  else if (l > 0.5) s = delta / (2 - sum);

  return {
    h,
    s: s * 100,
    l: l * 100
  };
}

rgb => hsv,参考

function rgb2hsv (r, g, b) {
  let h = 0;
  let s = 0;
  let v = 0;

  r = r / 255;
  g = g / 255;
  b = b / 255;

  const max = Math.max(r, g, b);
  const min = Math.min(r, g, b);
  const delta = max - min;

  if (max === min) h = 0;
  else if (max === r && g >= b) h = 60 * ((g - b) / delta);
  else if (max === r && g < b) h = 60 * ((g - b) / delta) + 360;
  else if (max === g) h = 60 * ((b - r) / delta) + 120;
  else if (max === b) h = 60 * ((r - g) / delta) + 240;

  if (max === 0) s = 0;
  else s = delta / max;

  v = max;

  return {
    h,
    s: s * 100,
    v: v * 100
  };
}

rgb => cmyk,参考

function rgb2cmyk (r, g, b) {
  let c = 0;
  let m = 0;
  let y = 0;
  let k = 0;

  r /= 255;
  g /= 255;
  b /= 255;

  c = 1 - r;
  m = 1 - g;
  y = 1 - b;
  k = Math.min(c, m, y);

  if (k === 1) {
    c = 0;
    m = 0;
    y = 0;
    k = 1;
  } else {
    c = (c - k) / (1 - k);
    m = (m - k) / (1 - k);
    y = (y - k) / (1 - k);
  }

  return {
    c: c * 100,
    m: m * 100,
    y: y * 100,
    k: k * 100
  };
}

rgb => xyz,参考

function rgb2xyz (r, g, b) {
  let x = 0;
  let y = 0;
  let z = 0;

  r /= 255;
  g /= 255;
  b /= 255;

  // 假定是 sRGB 色彩空间
  r = r <= 0.04045 ? (r / 12.92) : Math.pow((r + 0.055) / 1.055, 2.4);
  g = g <= 0.04045 ? (g / 12.92) : Math.pow((g + 0.055) / 1.055, 2.4);
  b = b <= 0.04045 ? (b / 12.92) : Math.pow((b + 0.055) / 1.055, 2.4);

  x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805);
  y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722);
  z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505);

  return {
    x: x * 100,
    y: y * 100,
    z: z * 100
  };
}

HEX

简介

RGB 颜色模型可以表示为 16 进制,可以分别用单个数字或两个数字表示,CSS 中需要在前面加上 # 号:

color: #3ea;
color: #33eeaa;

需要透明度时,值为 0 ~ 255 的数,用 16 进制表示,跟在最后:

color: #3ea9;
color: #33eeaa99;

转换

hex => rgb:

function hex2rgb (hex) {
  const res = /^#?([a-f0-9]{6}|[a-f0-9]{3})$/i.exec(hex);
  let str = '';

  if (!res) return [0, 0, 0];

  str = res[1];

  if (str.length === 3) {
    str = str.split('').map(char => (char + char)).join('');
  }

  return {
    r: parseInt(str.substr(0, 2), 16),
    g: parseInt(str.substr(2, 2), 16),
    b: parseInt(str.substr(4, 2), 16)
  };
}

HSL

简介

HSL 即色相(hue),饱和度(saturation),以及亮度(lightness)。

hsl

色相,代表人眼所能感知的颜色范围。将这些颜色分布在圆上,用圆心角来选择颜色,比如:60° 是黄色,120° 是绿色。有些 hsl 取色器中不是用圆来表示色相,而是直线,也代表 0° ~ 360°。

饱和度,即颜色的纯度,用百分比表示。往颜色里混入灰色,100% 代表颜色纯净,没加灰色;0% 代表全是灰色。

亮度(明度),指颜色明亮程度,用百分比表示。50% 时无添加,50% => 0%,加入的黑色逐渐增加,变暗;50% => 100%,加入的白色逐渐增加,变亮。

下图是 windows 画图程序的取色器,色盘里左右代表色相,上下代表饱和度,最右边的滑条上下表示亮度。

hsl 取色器

在 css 中使用

hsl(120, 50%, 50%);
hsla(120, 50%, 50%, 0.3); /* 透明度 0.3 */

转换

hsl => rgb,参考

function hsl2rgb (h, s, l) {
  let r = 0;
  let g = 0;
  let b = 0;
  let t1 = 0;
  let t2 = 0;

  const hue2rgb = (t1, t2, hue) => {
    if (hue < 0) hue += 1;
    if (hue >= 1) hue -= 1;
    if (hue < 1/6) return (t2 - t1) * (hue * 6) + t1;
    else if (hue < 1/2) return t2;
    else if (hue < 2/3) return (t2 - t1) * (4 - (hue * 6)) + t1;
    else return t1;
  };

  h = h / 360;
  s = s / 100;
  l = l / 100;

  if (s === 0) {
    r = g = b = l;
  } else {
    t2 = (l <= 0.5) ? (l * (s + 1)) : (l + s - (l * s));
    t1 = (l * 2) - t2;
    r = hue2rgb(t1, t2, h + 1/3);
    g = hue2rgb(t1, t2, h);
    b = hue2rgb(t1, t2, h - 1/3);
  }

  return {
    r: r * 255,
    g: g * 255,
    b: b * 255
  };
}

hsl => hsv,参考

function hsl2hsv (h, s, l) {
  let max = 0;
  let min = 0;
  let v = 0;

  s = s / 100;
  l = l / 100;

  if (l === 0) {
    max = min = 0;
  } else if (s === 0) {
    max = min = l;
  } else if (l > 0.5) {
    max = ((2 * l) + (s * (2 - (2 * l)))) / 2;
    min = ((2 * l) - (s * (2 - (2 * l)))) / 2;
  } else if (0 < l && l <= 0.5) {
    max = ((2 * l) + (s * 2 * l)) / 2;
    min = ((2 * l) - (s * 2 * l)) / 2;
  }

  if (max === 0) s = 0;
  else s = (max - min) / max;

  v = max;

  return {
    h,
    s: s * 100,
    v: v * 100
  };
}

HSV

简介

HSV 即色相(hue),饱和度(saturation),以及明度(brightness),也叫 HSB。

hsv

色相的意义与 HSL 的一致。

饱和度代表颜色中混入黑色的量,用百分比表示,0% 代表全是黑色;100% 代表是纯色,无黑色。

明度(亮度)代表颜色中混入白色的量,0% 代表全是白色;100% 代表是纯色,无白色。

下图是 PhotoShop 中的 HSV 取色器,色盘左右方向是明度变化,上下方向是饱和度变化,右边滑块上下是色相变化。

hsv 取色器

转换

hsv => rgb,参考

function hsv2rgb (h, s, v) {
  let p = 0;
  let q = 0;
  let t = 0;
  let h1 = 0;
  let f = 0;
  let rgb = [];

  s = s / 100;
  v = v / 100;

  h1 = Math.floor(h / 60);
  f = h / 60 - h1;
  p = v * (1 - s);
  q = v * (1 - f * s);
  t = v * (1 - (1 - f) * s);

  if (h1 === 0) rgb = [v, t, p];
  else if (h1 === 1) rgb = [q, v, p];
  else if (h1 === 2) rgb = [p, v, t];
  else if (h1 === 3) rgb = [p, q, v];
  else if (h1 === 4) rgb = [t, p, v];
  else if (h1 === 5) rgb = [v, p, q];

  return {
    r: rgb[0] * 255,
    g: rgb[1] * 255,
    b: rgb[2] * 255
  };
}

hsv => hsl,参考

function hsv2hsl (h, s, v) {
  let max = 0;
  let min = 0;
  let l = 0;

  s /= 100;
  v /= 100;

  max = v;
  min = (1 - s) * v;

  l = (max + min) / 2;

  if (l === 0 || max === min) s = 0;
  else if (0 < l && l <= 0.5) s = (max - min) / (2 * l);
  else if (l > 0.5) s = (max - min) / (2 - 2 * l);

  return {
    h,
    s: s * 100,
    l: l * 100
  };
}

hsv => hwb,参考

function hsv2hwb (h, s, v) {
  const w = ((100 - s) * v) / 100;
  const b = 100 - v;
  return {
    h,
    w,
    b
  };
}

CSS 命名颜色

简介

CSS 规范中定义了许多有名字的颜色,比如 white 是白色,即 rgb(255, 255, 255)#ffffff,详细列表见 MDN

HWB

简介

HWB 即色相(hue),白色(white),以及黑色(black)。

色相与 HSL 中的一致。

白色即颜色中添加白色的比例,用百分比表示,0% 即无白色,100% 即全白色。

黑色即颜色中添加黑色的比例,用百分比表示,0% 即无黑色,100% 即全黑色。

注意:如果白色与黑色之和大于 100%,将会等比缩小至和为 100%,此时只是显示灰色,色相的值被忽略。

在 CSS 中使用

HWB 是 CSS 4 草案 中的推荐颜色类型,现在(2018-03-29)主流的浏览器尚未支持,如果草案通过的话,相信未来会被浏览器支持。

在 CSS 中使用 HWB 颜色的示例:

color: hwb(120, 50%, 30%);
color: hwb(120, 50%, 30%, 0.4); /* 透明度 0.4 */

转换

hwb => rgb,参考

function hwb2rgb (h, w, b) {
  const rgb = hsl2rgb(h, 100, 50);

  w = w / 100;
  b = b / 100;

  if (w + b > 1) {
    w /= (w + b);
    b /= (w + b);
  }

  for (const key in rgb) {
    rgb[key] /= 255;
    rgb[key] *= (1 - w - b);
    rgb[key] += w;
  }

  return {
    r: rgb.r * 255,
    g: rgb.g * 255,
    b: rgb.b * 255
  };
}

hwb => hsv,参考

function hwb2hsv (h, w, b) {
  let v = 0;
  let s = 0;

  if (w + b > 100) {
    w *= (100 / (w + b));
    b *= (100 / (w + b));
  }

  v = 100 - b;
  s = 100 - (w / v) * 100;

  return {
    h,
    s,
    v
  };
}

CMYK

简介

CMYK 印刷四分色模式是彩色印刷时采用的一种色彩模式,原理是三原色(青色、洋红色、黄色)与黑色四种颜色叠加, 是一种 减色模型。CMYK 模式仅有 101^4 共 1030402 种色彩,RGB 有 256^3 共 16777216 种色彩。

在 CSS 中使用

CMYK 是 CSS 4 草案 中的推荐颜色类型,现在(2018-03-29)主流的浏览器尚未支持,如果草案通过的话,相信未来会被浏览器支持。

以下是在 CSS 中使用 CMYK 函数的示例:

color: device-cmyk(20%, 81%, 81%, 30%);
color: device-cmyk(20%, 81%, 81%, 30%, 0.3); /* 透明度 0.3 */

转换

cmyk => rgb,参考

function cmyk2rgb (c, m, y, k) {
  let r = 0;
  let g = 0;
  let b = 0;

  c /= 100;
  m /= 100;
  y /= 100;
  k /= 100;

  r = 1 - (c * (1 - k) + k);
  g = 1 - (m * (1 - k) + k);
  b = 1 - (y * (1 - k) + k);

  return {
    r: r * 255,
    g: g * 255,
    b: b * 255
  };
}

LAB

简介

LAB 是 CIE L*a*b*(CIELAB)的简称,由 国际照明委员会 以描述人眼所见的所有颜色为目的,基于 XYZ 色彩空间创建的。Lab颜色被设计来接近人类视觉,它致力于感知均匀性。

其中:

L 代表光的强度,0 最暗,为黑色;100 最亮,为白色。

A 代表红色和蓝色之间的位置,负值指绿色;正值指红色。

B 代表黄色和蓝色之间的位置,负值指蓝色;正值指黄色。

转换

lab => xyz,参考

function lab2xyz (l, a, b) {
  const xn = 95.047;
  const yn = 100;
  const zn = 108.883;

  const g = 6 / 29;

  const fy = (l + 16) / 116;
  const fx = fy + a / 500;
  const fz = fy - b / 200;

  let x = 0;
  let y = 0;
  let z = 0;

  x = fx > g ? Math.pow(fx, 3) : (fx - 16 / 116) * 3 * Math.pow(g, 2);
  y = fy > g ? Math.pow(fy, 3) : (fy - 16 / 116) * 3 * Math.pow(g, 2);
  z = fz > g ? Math.pow(fz, 3) : (fz - 16 / 116) * 3 * Math.pow(g, 2);

  x *= xn;
  y *= yn;
  z *= zn;

  return {
    x,
    y,
    z
  };
}

XYZ

简介

CIE 1931 XYZ色彩空间是由 国际照明委员会 于 1931 年创立的。它以人类对色彩的感知表示颜色,XYZ 是三色刺激值,分别代表红色、蓝色、绿色所导出的参数。

转换

xyz => lab,参考

function xyz2lab (x, y, z) {
  const xn = 95.047;
  const yn = 100;
  const zn = 108.883;

  const g = Math.pow(6 / 29, 3);
  const fn = (n) => ((1 / 3) * Math.pow(29 / 6, 2) * n + (16 / 116));

  let fx = 0;
  let fy = 0;
  let fz = 0;
  let l = 0;
  let a = 0;
  let b = 0;

  x /= xn;
  y /= yn;
  z /= zn;

  fx = x > g ? Math.pow(x, 1 / 3) : fn(x);
  fy = y > g ? Math.pow(y, 1 / 3) : fn(y);
  fz = z > g ? Math.pow(z, 1 / 3) : fn(z);

  l = (116 * fx) - 16;
  a = 500 * (fx - fy);
  b = 200 * (fy - fz);

  return {
    l,
    a,
    b
  };
}

xyz => rgb,参考

function xyz2rgb (x, y, z) {
  let r = 0;
  let g = 0;
  let b = 0;

  const fn = (n) => (1.055 * Math.pow(n, 1 / 2.4) - 0.055);

  x /= 100;
  y /= 100;
  z /= 100;

  r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986);
  g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415);
  b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570);

  // 假定转到 sRGB 色彩空间
  r = r <= 0.0031308 ? 12.92 * r : fn(r);
  g = g <= 0.0031308 ? 12.92 * g : fn(g);
  b = b <= 0.0031308 ? 12.92 * b : fn(b);

  r = Math.min(Math.max(0, r), 1);
  g = Math.min(Math.max(0, g), 1);
  b = Math.min(Math.max(0, b), 1);

  return {
    r: r * 255,
    g: g * 255,
    b: b * 255
  };
}

总结

本文简单地介绍了一下各种颜色格式,以及之间的转换。需要注意的是,有些转换并没有提供,比如 rgb => lab,这时候需要先转换到中间值才能完成转换,比如 rgb => xyz => lab。转换代码中的数值一般是 0 ~ 255 或 0 ~ 100 范围的,使用时请按需修改,使之映射到你需要的范围。另外,文中代码示例基本上只是按照公式来写,并没有处理输入值的合法与否等工程问题,也没有做性能优化,实际运用到生产环境时请按需修改。

关于成熟的封装库,可以考虑 color.js,文中部分代码也是参考此库的源代码。

参考:

(完)

This post was published 1854 days ago, some content may be inaccurate. Edit it on GitHub
Comment loading