Skip to content

◷ 发表于: 2025-03-18

◷ 更新于: 2025-03-27

🅆 字数: 0

颜色转换

javascript
const t0 = 4 / 29
const t1 = 6 / 29
const t2 = 3 * t1 * t1
const t3 = t1 * t1 * t1
const Xn = 0.96422
const Yn = 1
const Zn = 0.82521

// color: {r,g,b} ==> {h,c,l}
export function rgbToHcl (color) {
  return labToHcl(rbgToLab(color))
}

// color: {h,c,l} ==> {r,g,b}
export function hclToRgb (color) {
  return labToRgb(hclToLab(color))
}

// {r,g,b} ==> {l,a,b}
function rbgToLab (o) {
  const r = rgb2lrgb(o.r)
  const g = rgb2lrgb(o.g)
  const b = rgb2lrgb(o.b)
  const y = xyz2lab((0.2225045 * r + 0.7168786 * g + 0.0606169 * b) / 1)
  const x = r === g && g === b ? y : xyz2lab((0.4360747 * r + 0.3850649 * g + 0.1430804 * b) / 0.96422)
  const z = r === g && g === b ? y : xyz2lab((0.0139322 * r + 0.0971045 * g + 0.7141733 * b) / 0.82521)

  return {
    l: 116 * y - 16,
    a: 500 * (x - y),
    b: 200 * (y - z)
  }
}

// {l,a,b} ==> {r,g,b}
function labToRgb ({ l, a, b }) {
  let y = (l + 16) / 116
  let x = isNaN(a) ? y : y + a / 500
  let z = isNaN(b) ? y : y - b / 200

  x = Xn * lab2xyz(x)
  y = Yn * lab2xyz(y)
  z = Zn * lab2xyz(z)

  return {
    r: Math.round(lrgb2rgb(3.1338561 * x - 1.6168667 * y - 0.4906146 * z)),
    g: Math.round(lrgb2rgb(-0.9787684 * x + 1.9161415 * y + 0.0334540 * z)),
    b: Math.round(lrgb2rgb(0.0719453 * x - 0.2289914 * y + 1.4052427 * z))
  }
}

// {l,a,b} ==> {h,c,l}
function labToHcl ({ l, a, b }) {
  const h = Math.atan2(b, a) * (180 / Math.PI)

  return {
    h: h < 0 ? h + 360 : h,
    c: Math.sqrt(a * a + b * b),
    l: l
  }
}

// {h,c,l} ==> {l,a,b}
function hclToLab ({ h, c, l }) {
  const h2 = h * (Math.PI / 180)

  return {
    l,
    a: Math.cos(h2) * c,
    b: Math.sin(h2) * c
  }
}

function rgb2lrgb (x) {
  return (x /= 255) <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4)
}

function lrgb2rgb (x) {
  return 255 * (x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055)
}

function xyz2lab (t) {
  return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0
}

function lab2xyz (t) {
  return t > t1 ? t * t * t : t2 * (t - t0)
}

function hexToRgb (hex) {
  let m = /^#([0-9a-fA-F]{3,8})$/.exec(hex)
  if (!(m && m[1] && m[1].length === 6)) return null

  m = parseInt(m[1], 16)
  return { r: m >> 16 & 0xff, g: m >> 8 & 0xff, b: m & 0xff, a: 1 }
}

function hex (value) {
  value = Math.max(0, Math.min(255, Math.round(value) || 0))
  return (value < 16 ? '0' : '') + value.toString(16)
}
function rgbToHex (o) {
  return `#${hex(o.r)}${hex(o.g)}${hex(o.b)}`)
}

基于 CC BY-NC-SA 4.0 许可发布