46 lines
2.0 KiB
TypeScript
46 lines
2.0 KiB
TypeScript
export interface Hsv { h: number; s: number; v: number }
|
|
|
|
const clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value));
|
|
const byte = (value: number) => Math.round(clamp(value, 0, 255)).toString(16).padStart(2, '0');
|
|
|
|
/** "#rgb" / "#rrggbb"(大小写均可)转成小写 "#rrggbb",其他返回 undefined */
|
|
export function parseHex(text: string): string | undefined {
|
|
const m = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(text.trim());
|
|
if (!m) return undefined;
|
|
const hex = m[1]!.toLowerCase();
|
|
return `#${hex.length === 3 ? [...hex].map((c) => c + c).join('') : hex}`;
|
|
}
|
|
|
|
export function hexToHsv(hex: string): Hsv {
|
|
const value = parseHex(hex) ?? '#000000';
|
|
const r = parseInt(value.slice(1, 3), 16) / 255; const g = parseInt(value.slice(3, 5), 16) / 255; const b = parseInt(value.slice(5, 7), 16) / 255;
|
|
const max = Math.max(r, g, b); const delta = max - Math.min(r, g, b);
|
|
let h = 0;
|
|
if (delta) {
|
|
if (max === r) h = ((g - b) / delta) % 6;
|
|
else if (max === g) h = (b - r) / delta + 2;
|
|
else h = (r - g) / delta + 4;
|
|
h = (h * 60 + 360) % 360;
|
|
}
|
|
return { h, s: max ? delta / max : 0, v: max };
|
|
}
|
|
|
|
export function hsvToHex({ h, s, v }: Hsv): string {
|
|
const f = (n: number) => { const k = (n + h / 60) % 6; return v - v * s * Math.max(0, Math.min(k, 4 - k, 1)); };
|
|
return `#${byte(f(5) * 255)}${byte(f(3) * 255)}${byte(f(1) * 255)}`;
|
|
}
|
|
|
|
/**
|
|
* 从一段文字里取出颜色:兼容在线渐变生成器复制出来的 CSS(linear-gradient(90deg, #ff8a3d 0%, rgb(240,54,107) 100%) 之类)。
|
|
* 按出现顺序最多取 max 个,透明度忽略。
|
|
*/
|
|
export function extractColors(text: string, max = 5): string[] {
|
|
const found: string[] = [];
|
|
const re = /#([0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{3})\b|rgba?\(\s*(\d{1,3})[\s,]+(\d{1,3})[\s,]+(\d{1,3})/gi;
|
|
for (let m = re.exec(text); m && found.length < max; m = re.exec(text)) {
|
|
if (m[1]) found.push(parseHex(m[1].length === 8 ? m[1].slice(0, 6) : m[1])!);
|
|
else found.push(`#${byte(Number(m[2]))}${byte(Number(m[3]))}${byte(Number(m[4]))}`);
|
|
}
|
|
return found;
|
|
}
|