@leafer-in/color
Version:
169 lines (133 loc) • 4.79 kB
text/typescript
import { IColor, IObject, IRGBA } from '@leafer-ui/interface'
import { colorNames } from './colors'
const rgbMatch = /^rgb\((\d+),\s*(\d+),\s*(\d+)/i
const rgbaMatch = /^rgba\((\d+),\s*(\d+),\s*(\d+),\s*(\d*\.?\d+)/i
const hslMatch = /^hsl\((\d+),\s*(\d+)%\s*,\s*(\d+)%/i
const hslaMatch = /^hsla\((\d+),\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d*\.?\d+)/i
const int = parseInt, float = parseFloat, { round } = Math
let cache: IObject = {}, totalCache = 0
export function colorToRGBA(color: IColor, opacity?: number): IRGBA {
let RGBA: IRGBA
let useOpacity = opacity !== undefined && opacity !== 1
if (typeof color === 'string') {
const cacheColor = cache[color]
if (cacheColor) {
RGBA = { ...cacheColor }
} else {
switch (color[0]) {
case '#':
RGBA = hexToRGBA(color)
break
case 'R':
case 'r':
if (color[4] === '(' && rgbaMatch.test(color)) RGBA = rgbaToRGBA(color)
else if (color[3] === '(' && rgbMatch.test(color)) RGBA = rgbToRGBA(color)
break
case 'H':
case 'h':
if (color[4] === '(' && hslaMatch.test(color)) RGBA = hslaToRGBA(color)
else if (color[3] === '(' && hslMatch.test(color)) RGBA = hslToRGBA(color)
break
}
if (!RGBA) {
const value = colorNames[color.toLowerCase()]
if (value) RGBA = hexToRGBA('#' + value)
}
if (RGBA) {
totalCache++
if (totalCache > 10000) cache = {}, totalCache = 0
cache[color] = { ...RGBA }
}
}
} else if (typeof color === 'object') {
if (color.a === undefined) color.a = 1
if (useOpacity) color = { ...color }
RGBA = color as IRGBA
}
if (!RGBA) RGBA = { r: 255, g: 255, b: 255, a: 1 }
if (useOpacity) RGBA.a *= opacity
return RGBA
}
function hexToRGBA(color: string): IRGBA {
let r, g, b, a = 1
switch (color.length) {
case 9: // #FF0000FF
r = int(color.slice(1, 3), 16)
g = int(color.slice(3, 5), 16)
b = int(color.slice(5, 7), 16)
a = int(color.slice(7, 9), 16) / 255
break
case 7: // #FF0000
r = int(color.slice(1, 3), 16)
g = int(color.slice(3, 5), 16)
b = int(color.slice(5, 7), 16)
break
case 5: // #F00F => #FF0000FF
r = int(color[1] + color[1], 16)
g = int(color[2] + color[2], 16)
b = int(color[3] + color[3], 16)
a = int(color[4] + color[4], 16) / 255
break
case 4: // #F00 => #FF0000
r = int(color[1] + color[1], 16)
g = int(color[2] + color[2], 16)
b = int(color[3] + color[3], 16)
break
case 3: // #F0 => #F0F0F0 非标准
r = g = b = int(color[1] + color[2], 16)
break
case 2: // #F => #FFFFFF 非标准
r = g = b = int(color[1] + color[1], 16)
break
}
return { r, g, b, a }
}
function rgbToRGBA(color: string): IRGBA {
const match = rgbMatch.exec(color) // rgb(255, 255, 255)
return {
r: int(match[1]),
g: int(match[2]),
b: int(match[3]),
a: 1
}
}
function rgbaToRGBA(color: string): IRGBA {
const match = rgbaMatch.exec(color) // rgba(255, 255, 255, 1)
return {
r: int(match[1]),
g: int(match[2]),
b: int(match[3]),
a: float(match[4])
}
}
function hslToRGBA(color: string): IRGBA {
const match = hslMatch.exec(color) // hsl(360,100%, 100%)
return hsla(float(match[1]), float(match[2]), float(match[3]), 1)
}
function hslaToRGBA(color: string): IRGBA {
const match = hslaMatch.exec(color) // hsl(360,100%, 100%, 1)
return hsla(float(match[1]), float(match[2]), float(match[3]), float(match[4]))
}
const n1 = 1 / 6, n2 = 0.5, n3 = 2 / 3, n4 = 1 / 3
function hue(p: number, q: number, t: number) {
if (t < 0) t++
else if (t > 1) t--
if (t < n1) return p + (q - p) * 6 * t
if (t < n2) return q
if (t < n3) return p + (q - p) * (n3 - t) * 6
return p
}
function hsla(h: number, s: number, l: number, a = 1): IRGBA {
let r, g, b
h /= 360, s /= 100, l /= 100
if (s === 0) {
r = g = b = l
} else {
let q = l < 0.5 ? l * (1 + s) : l + s - l * s
let p = 2 * l - q
r = hue(p, q, h + n4)
g = hue(p, q, h)
b = hue(p, q, h - n4)
}
return { r: round(r * 255), g: round(g * 255), b: round(b * 255), a }
}