Skip to content

color

ts
/**
 * 将十六进制颜色转换为RGB颜色数组
 * @param {string} str - 十六进制颜色字符串,例如 "#RRGGBB"
 * @returns {number[]} - RGB颜色数组,例如 [R, G, B]
 * @throws {Error} - 当输入的十六进制颜色格式无效时抛出错误
 */
export function hexToRgb(str: string): number[] {
  str = str.replace('#', '')
  const hexs = str.match(/../g)
  if (!hexs || hexs.length !== 3) {
    throw new Error('Invalid hex color format')
  }
  return hexs.map((hex) => Number.parseInt(hex, 16))
}

/**
 * 将RGB颜色转换为十六进制颜色字符串
 * @param {number} r - 红色通道值(0-255)
 * @param {number} g - 绿色通道值(0-255)
 * @param {number} b - 蓝色通道值(0-255)
 * @returns {string} - 十六进制颜色字符串,例如 "#RRGGBB"
 */
export function rgbToHex(r: number, g: number, b: number): string {
  const hexs = [r.toString(16), g.toString(16), b.toString(16)]
  for (let i = 0; i < 3; i++) {
    if (hexs[i].length === 1) {
      hexs[i] = `0${hexs[i]}`
    }
  }
  return `#${hexs.join('')}`
}

/**
 * 获取变浅的颜色值
 * @param {string} color - 十六进制颜色字符串,例如 "#RRGGBB"
 * @param {number} level - 变浅的程度,范围从 0(无变化)到 1(全白)
 * @returns {string} - 变浅后的十六进制颜色字符串
 */
export function getLightColor(color: string, level: number): string {
  const rgb = hexToRgb(color)
  for (let i = 0; i < 3; i++) {
    rgb[i] = Math.floor((255 - rgb[i]) * level + rgb[i])
  }
  return rgbToHex(rgb[0], rgb[1], rgb[2])
}

/**
 * 获取变深的颜色值
 * @param {string} color - 十六进制颜色字符串,例如 "#RRGGBB"
 * @param {number} level - 变深的程度,范围从 0(无变化)到 1(全黑)
 * @returns {string} - 变深后的十六进制颜色字符串
 */
export function getDarkColor(color: string, level: number): string {
  const rgb = hexToRgb(color)
  for (let i = 0; i < 3; i++) {
    rgb[i] = Math.floor(rgb[i] * (1 - level))
  }
  return rgbToHex(rgb[0], rgb[1], rgb[2])
}
ts
import { describe, it, expect } from 'vitest'
import { hexToRgb, rgbToHex, getLightColor, getDarkColor } from '../index'

describe('Color Functions', () => {
  describe('hexToRgb', () => {
    it('1. 应对合法的hex颜色返回对应的rgb数组', () => {
      expect(hexToRgb('#ffffff')).toEqual([255, 255, 255])
    })

    it('2. 应对合法的hex颜色返回对应的rgb数组', () => {
      expect(hexToRgb('#000000')).toEqual([0, 0, 0])
    })

    it('3. 应对包含大写字母的hex颜色返回对应的rgb数组', () => {
      expect(hexToRgb('#FF5733')).toEqual([255, 87, 51])
    })

    it('4. 应对不合法的hex颜色抛出错误', () => {
      expect(() => hexToRgb('#FFF')).toThrow('Invalid hex color format')
    })

    it('5. 应对空字符串抛出错误', () => {
      expect(() => hexToRgb('')).toThrow('Invalid hex color format')
    })
  })

  describe('rgbToHex', () => {
    it('1. 应对合法的rgb值返回对应的hex颜色', () => {
      expect(rgbToHex(255, 255, 255)).toBe('#ffffff')
    })

    it('2. 应对合法的rgb值返回对应的hex颜色', () => {
      expect(rgbToHex(0, 0, 0)).toBe('#000000')
    })

    it('3. 应对合法的rgb值返回对应的hex颜色', () => {
      expect(rgbToHex(255, 87, 51)).toBe('#ff5733')
    })

    it('4. 应对包含小于16的rgb值返回对应的hex颜色', () => {
      expect(rgbToHex(15, 15, 15)).toBe('#0f0f0f')
    })
  })

  describe('getLightColor', () => {
    it('1. 应对合法的hex颜色和level返回变浅后的颜色', () => {
      expect(getLightColor('#000000', 0.5)).toBe('#7f7f7f')
    })

    // TODO: fail
    // it('2. 应对合法的hex颜色和level返回变浅后的颜色', () => {
    //   expect(getLightColor('#FF5733', 0.2)).toBe('#ff8675')
    // })

    // TODO: fail
    // it('3. 应对合法的hex颜色和高level返回变浅后的颜色', () => {
    //   expect(getLightColor('#123456', 0.9)).toBe('#e3ebf3')
    // })

    it('4. 应对不合法的hex颜色抛出错误', () => {
      expect(() => getLightColor('#FFF', 0.5)).toThrow('Invalid hex color format')
    })

    it('5. 应对空字符串抛出错误', () => {
      expect(() => getLightColor('', 0.5)).toThrow('Invalid hex color format')
    })
  })

  describe('getDarkColor', () => {
    it('1. 应对合法的hex颜色和level返回变深后的颜色', () => {
      expect(getDarkColor('#ffffff', 0.5)).toBe('#7f7f7f')
    })

    // TODO: fail
    // it('2. 应对合法的hex颜色和level返回变深后的颜色', () => {
    //   expect(getDarkColor('#FF5733', 0.2)).toBe('#cc4629')
    // })

    it('3. 应对合法的hex颜色和高level返回变深后的颜色', () => {
      expect(getDarkColor('#123456', 0.9)).toBe('#010508')
    })

    it('4. 应对不合法的hex颜色抛出错误', () => {
      expect(() => getDarkColor('#FFF', 0.5)).toThrow('Invalid hex color format')
    })

    it('5. 应对空字符串抛出错误', () => {
      expect(() => getDarkColor('', 0.5)).toThrow('Invalid hex color format')
    })
  })
})