111 lines
2.8 KiB
TypeScript
111 lines
2.8 KiB
TypeScript
// RGB 转 HSL
|
|
export function rgbToHsl(rgb: number[]) {
|
|
if (rgb.length !== 3) {
|
|
throw new Error('Input must be an array of three numbers representing RGB values.');
|
|
}
|
|
let [r, g, b] = rgb;
|
|
r /= 255;
|
|
g /= 255;
|
|
b /= 255;
|
|
const max = Math.max(r, g, b), min = Math.min(r, g, b);
|
|
let h = 0, s = 0;
|
|
const l = (max + min) / 2;
|
|
|
|
if (max !== min) {
|
|
const d = max - min;
|
|
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
switch (max) {
|
|
case r:
|
|
h = (g - b) / d + (g < b ? 6 : 0);
|
|
break;
|
|
case g:
|
|
h = (b - r) / d + 2;
|
|
break;
|
|
case b:
|
|
h = (r - g) / d + 4;
|
|
break;
|
|
}
|
|
h /= 6;
|
|
}
|
|
return [h * 360, s * 100, l * 100];
|
|
}
|
|
|
|
// HSL 转 RGB
|
|
export function hslToRgb(hsl: number[]) {
|
|
if (hsl.length !== 3) {
|
|
throw new Error('Input must be an array of three numbers representing HSL values.');
|
|
}
|
|
let [h, s, l] = hsl;
|
|
h /= 360;
|
|
s /= 100;
|
|
l /= 100;
|
|
let r, g, b;
|
|
|
|
if (s === 0) {
|
|
r = g = b = l;
|
|
} else {
|
|
const hue2rgb = (p: number, q: number, t: number) => {
|
|
if (t < 0) t += 1;
|
|
if (t > 1) t -= 1;
|
|
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
|
if (t < 1 / 2) return q;
|
|
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
|
return p;
|
|
};
|
|
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
const p = 2 * l - q;
|
|
r = hue2rgb(p, q, h + 1 / 3);
|
|
g = hue2rgb(p, q, h);
|
|
b = hue2rgb(p, q, h - 1 / 3);
|
|
}
|
|
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
|
|
}
|
|
|
|
export function toRGBArray(color: string): number[] {
|
|
// 处理 rgb/rgba
|
|
const rgbMatch = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*[\d.]+)?\)/);
|
|
if (rgbMatch) {
|
|
return [parseInt(rgbMatch[1]), parseInt(rgbMatch[2]), parseInt(rgbMatch[3])];
|
|
}
|
|
// 处理 #fff 或 #ffffff
|
|
const hexMatch = color.match(/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/);
|
|
if (hexMatch) {
|
|
let hex = hexMatch[1];
|
|
if (hex.length === 3) {
|
|
hex = hex.split('').map((x) => x + x).join('');
|
|
}
|
|
const num = parseInt(hex, 16);
|
|
return [
|
|
(num >> 16) & 255,
|
|
(num >> 8) & 255,
|
|
num & 255,
|
|
];
|
|
}
|
|
throw new Error('Invalid color format');
|
|
}
|
|
|
|
export function toHexString(rgb: number[]): string {
|
|
let ans = '#';
|
|
for (const value of rgb) {
|
|
if (value < 0 || value > 255) {
|
|
throw new Error('RGB values must be in the range 0-255.');
|
|
}
|
|
ans += value.toString(16).padStart(2, '0');
|
|
}
|
|
return ans.toUpperCase();
|
|
}
|
|
|
|
export function toLightColor(rgb: number[]): number[] {
|
|
const hsl = rgbToHsl(rgb);
|
|
if (hsl[2] < 50)
|
|
hsl[2] = hsl[2] / 5 + 50; // 增加亮度
|
|
return hslToRgb(hsl);
|
|
}
|
|
|
|
export function toDarkColor(rgb: number[]): number[] {
|
|
const hsl = rgbToHsl(rgb);
|
|
if (hsl[2] > 50)
|
|
hsl[2] = 50 - (hsl[2] - 50) / 5; // 减少亮度
|
|
return hslToRgb(hsl);
|
|
}
|