跳转到内容

颜色转换

HEX 转 RGB

ts
/**
 * HEX 转 RGB
 * @param hex - HEX 颜色值,支持 #RGB、#RRGGBB 格式
 * @returns RGB 颜色对象
 */
export function hexToRgb(hex: string) {
  // 移除 # 前缀
  const normalizedHex = hex.replace('#', '')

  // 处理 #RGB 格式(简写)
  if (normalizedHex.length === 3) {
    const r = parseInt(normalizedHex[0] + normalizedHex[0], 16)
    const g = parseInt(normalizedHex[1] + normalizedHex[1], 16)
    const b = parseInt(normalizedHex[2] + normalizedHex[2], 16)
    return { r, g, b }
  }
  // 处理 #RRGGBB 格式
  else if (normalizedHex.length === 6) {
    const r = parseInt(normalizedHex.substring(0, 2), 16)
    const g = parseInt(normalizedHex.substring(2, 4), 16)
    const b = parseInt(normalizedHex.substring(4, 6), 16)
    return { r, g, b }
  }
  // 无效格式
  else {
    throw new Error(`Invalid HEX color format: ${hex}`)
  }
}

HEX 转 HSL

ts
import { hexToRgb } from './hex-to-rgb'
import { rgbToHsl } from './rgb-to-hsl'

/**
 * HEX 转 HSL
 * @param hex - HEX 颜色值,支持 #RGB、#RRGGBB 格式
 * @returns HSL 颜色对象
 */
export function hexToHsl(hex: string) {
  const rgb = hexToRgb(hex)
  return rgbToHsl(rgb)
}

RGB 转 HEX

ts
/**
 * RGB 转 HEX
 * @param rgb - RGB 颜色对象
 * @returns HEX 颜色值字符串,格式为 #RRGGBB
 */
export function rgbToHex(rgb: { r: number; g: number; b: number }) {
  const { r, g, b } = rgb

  // 确保值在 0-255 范围内
  const clamp = (value: number) => Math.max(0, Math.min(255, Math.round(value)))

  // 转换为十六进制字符串并补零
  const toHex = (value: number) => {
    const hex = clamp(value).toString(16)
    return hex.length === 1 ? '0' + hex : hex
  }

  return `#${toHex(r)}${toHex(g)}${toHex(b)}`
}

RGB 转 HSL

ts
/**
 * RGB 转 HSL
 */
export function rgbToHsl(rgb: { r: number; g: number; b: number }) {
  const { r, g, b } = rgb

  // 归一化
  const rNorm = r / 255
  const gNorm = g / 255
  const bNorm = b / 255

  // 最大值、最小值、差值
  const max = Math.max(rNorm, gNorm, bNorm)
  const min = Math.min(rNorm, gNorm, bNorm)
  const delta = max - min

  // 亮度 L
  const l = (max + min) / 2

  // 饱和度 S
  let s = 0
  if (delta !== 0) {
    s = delta / (1 - Math.abs(2 * l - 1))
  }

  // 色相 H
  let h = 0
  if (delta !== 0) {
    if (rNorm >= gNorm && rNorm >= bNorm) {
      h = ((gNorm - bNorm) / delta) % 6
    } else if (gNorm >= bNorm) {
      h = (bNorm - rNorm) / delta + 2
    } else {
      h = (rNorm - gNorm) / delta + 4
    }
    h *= 60
    if (h < 0) h += 360
  }

  // 返回整数格式
  return {
    h: Math.round(h),
    s: Math.round(s * 100),
    l: Math.round(l * 100),
  }
}

HSL 转 RGB

ts
/**
 * HSL 转 RGB
 */
export function hslToRgb(hsl: { h: number; s: number; l: number }) {
  const { h, s, l } = hsl

  // 归一化 HSL 值
  const H = h / 60 // 将 0~360 转换为 0~6
  const S = s / 100
  const L = l / 100

  // 计算中间值
  const C = (1 - Math.abs(2 * L - 1)) * S
  const X = C * (1 - Math.abs((H % 2) - 1))
  const m = L - C / 2

  // 根据 H 的区间确定 RGB 分量
  let r, g, b

  if (H >= 0 && H < 1) {
    r = C
    g = X
    b = 0
  } else if (H >= 1 && H < 2) {
    r = X
    g = C
    b = 0
  } else if (H >= 2 && H < 3) {
    r = 0
    g = C
    b = X
  } else if (H >= 3 && H < 4) {
    r = 0
    g = X
    b = C
  } else if (H >= 4 && H < 5) {
    r = X
    g = 0
    b = C
  } else if (H >= 5 && H < 6) {
    r = C
    g = 0
    b = X
  } else {
    // 处理 H = 360 的情况(即 H = 0)
    r = 0
    g = 0
    b = 0
  }

  // 应用偏移量 m,转换为 0~1 范围
  r += m
  g += m
  b += m

  // 转换为 0~255 的整数范围
  return {
    r: Math.round(r * 255),
    g: Math.round(g * 255),
    b: Math.round(b * 255),
  }
}

HSL 转 HEX

ts
import { hslToRgb } from './hsl-to-rgb'
import { rgbToHex } from './rgb-to-hex'

/**
 * HSL 转 HEX
 * @param hsl - HSL 颜色对象
 * @returns HEX 颜色值字符串,格式为 #RRGGBB
 */
export function hslToHex(hsl: { h: number; s: number; l: number }) {
  const rgb = hslToRgb(hsl)
  return rgbToHex(rgb)
}