js/ts颜色格式转换
目前主流浏览器支持度的颜色为关键字
/ HEX
/ RGB
/ HSL
, 新的色彩函数HWB
/LAB
/LCH
支持度并不高, 因此不在本文讨论范围内
效果如下:
色彩语法简介:
-
关键字
颜色关键字: 如red / black
transparent
关键字: 表示透明currentColor
关键字: 表示继承自上级的颜色 -
HEX / HEXA 十六进制符号
使用RGB的16进制表示, 完整表示为#RRGGBB[AA], 其中AA表示透明度, 范围0-255(00-FF), 默认为FF
另一种表示方式为
#RGB[A]
, 可选值为(0-F), 为上述的缩写版, 如#F09
表示#FF0099
-
RGB / RGBA函数
语法:
rgb[a](R, G, B[, A])
或者rgb[a](R G B / A)
, 即逗号表示法和空格表示法 -
HSL / HSLA函数
与HEX / RGB不同, 它们被称为RGB颜色, 即RED-GREEN-BLUE-ALPHA(红绿蓝-透明度)
HSL则表示
色相 - 饱和度 - 亮度(Hue-saturation-lightness)
, 在可视化调整界面, HSL将比RGB更直观语法:
hsl[a](H, S, L[, A])
或者hsl[a](H S L / A)
, 即逗号表示法和空格表示法其中H没有单位时, 表示度数(0-360), 如0deg和360deg表示红色, 绿色为120, 蓝色为240, 并且支持负数, 如 -120deg=240deg, 480deg=120deg, -1turn = 1turn等
S(饱和度)和L(亮度)与A(透明度)则使用百分比 / 小数表示
-
此外以上介绍仅仅是一个简介, 具体详细规则可参考MDN
代码奉上:
这里使用了一个小技巧, 颜色关键字 / hex / hsl / rgb为了保持代码简洁易懂, 使用浏览器来辅助解析, 虽然会有稍微的一点性能损耗, 但是却大幅降低了代码量
export class LyColor {
// red
private readonly r: number
// green
private readonly g: number
// blue
private readonly b: number
// alpha
private readonly a: number
constructor(color: string) {
const div = document.createElement('div')
document.body.appendChild(div)
div.style.color = color
const computedColor = window.getComputedStyle(div).color
document.body.removeChild(div)
const [r = 0, g = 0, b = 0, a = 255] = computedColor.match(/(\d+%?)/g) ?? []
this.r = +r
this.g = +g
this.b = +b
this.a = +a
}
toHex() {
return `#${LyColor.#hex(this.r)}${LyColor.#hex(this.g)}${LyColor.#hex(this.b)}`
}
toHexA() {
return `#${LyColor.#hex(this.r)}${LyColor.#hex(this.g)}${LyColor.#hex(this.b)}${LyColor.#hex(this.a)}`
}
toRGB() {
return `rgb(${this.r} ${this.g} ${this.b})`
}
toRGBA() {
return `rgba(${this.r} ${this.g} ${this.b} / ${this.a})`
}
toHSL() {
const [h, s, l] = this.getHSLA()
return `hsl(${h} ${s}% ${l}%)`
}
toHSLA() {
const [h, s, l, a] = this.getHSLA()
return `hsla(${h} ${s}% ${l}% / ${a})`
}
getHSLA(): [h: number, s: number, l: number, a: number] {
const r = this.r / 255, g = this.g / 255, b = this.b / 255
let minC = Math.min(r, g, b), maxC = Math.max(r, g, b), delta = maxC - minC
let h: number, s: number, l: number
if (delta === 0) {
h = 0
} else if (maxC == r) {
h = ((g - b) / delta) % 6
} else if (maxC == g) {
h = (b - r) / delta + 2
} else {
h = (r - g) / delta + 4
}
h = Math.round(h * 60)
if (h < 0)
h += 360
l = (maxC + minC) / 2
s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1))
s = +(s * 100).toFixed(1)
l = +(l * 100).toFixed(1)
return [h, s, l, this.a]
}
getRGBA(): [r: number, g: number, b: number, a: number] {
return [this.r, this.g, this.b, this.a]
}
static #hex(decimal: number) {
const hex = decimal.toString(16)
return hex.length === 1 ? '0' + hex : hex
}
}
使用方式:
new LyColor('rgb(1, 2, 3)').toHex() // #010203
demo见github