goldenChat base source add
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
/** HEX · RGB · HSV 변환 (색상 팔레트 UI용) */
|
||||
|
||||
export interface Rgb {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
}
|
||||
|
||||
export interface Hsv {
|
||||
/** 0–360 */
|
||||
h: number;
|
||||
/** 0–1 */
|
||||
s: number;
|
||||
/** 0–1 */
|
||||
v: number;
|
||||
}
|
||||
|
||||
function clamp(n: number, min: number, max: number): number {
|
||||
return Math.max(min, Math.min(max, n));
|
||||
}
|
||||
|
||||
export function normalizeHex6(hex: string): string {
|
||||
const h = hex.trim();
|
||||
if (/^#[0-9A-Fa-f]{6}$/.test(h)) return h.toUpperCase();
|
||||
if (/^#[0-9A-Fa-f]{3}$/.test(h)) {
|
||||
const c = h.slice(1);
|
||||
return ('#' + c[0] + c[0] + c[1] + c[1] + c[2] + c[2]).toUpperCase();
|
||||
}
|
||||
return '#2962FF';
|
||||
}
|
||||
|
||||
export function hexToRgb(hex: string): Rgb {
|
||||
const h = normalizeHex6(hex).slice(1);
|
||||
return {
|
||||
r: parseInt(h.slice(0, 2), 16),
|
||||
g: parseInt(h.slice(2, 4), 16),
|
||||
b: parseInt(h.slice(4, 6), 16),
|
||||
};
|
||||
}
|
||||
|
||||
export function rgbToHex(r: number, g: number, b: number): string {
|
||||
const to = (n: number) => clamp(Math.round(n), 0, 255).toString(16).padStart(2, '0');
|
||||
return `#${to(r)}${to(g)}${to(b)}`.toUpperCase();
|
||||
}
|
||||
|
||||
export function rgbToHsv({ r, g, b }: Rgb): Hsv {
|
||||
const rn = r / 255;
|
||||
const gn = g / 255;
|
||||
const bn = b / 255;
|
||||
const max = Math.max(rn, gn, bn);
|
||||
const min = Math.min(rn, gn, bn);
|
||||
const d = max - min;
|
||||
let h = 0;
|
||||
if (d !== 0) {
|
||||
if (max === rn) h = ((gn - bn) / d) % 6;
|
||||
else if (max === gn) h = (bn - rn) / d + 2;
|
||||
else h = (rn - gn) / d + 4;
|
||||
h *= 60;
|
||||
if (h < 0) h += 360;
|
||||
}
|
||||
const s = max === 0 ? 0 : d / max;
|
||||
return { h, s, v: max };
|
||||
}
|
||||
|
||||
export function hsvToRgb({ h, s, v }: Hsv): Rgb {
|
||||
const c = v * s;
|
||||
const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
|
||||
const m = v - c;
|
||||
let rp = 0;
|
||||
let gp = 0;
|
||||
let bp = 0;
|
||||
if (h < 60) { rp = c; gp = x; }
|
||||
else if (h < 120) { rp = x; gp = c; }
|
||||
else if (h < 180) { gp = c; bp = x; }
|
||||
else if (h < 240) { gp = x; bp = c; }
|
||||
else if (h < 300) { rp = x; bp = c; }
|
||||
else { rp = c; bp = x; }
|
||||
return {
|
||||
r: Math.round((rp + m) * 255),
|
||||
g: Math.round((gp + m) * 255),
|
||||
b: Math.round((bp + m) * 255),
|
||||
};
|
||||
}
|
||||
|
||||
export function hexToHsv(hex: string): Hsv {
|
||||
return rgbToHsv(hexToRgb(hex));
|
||||
}
|
||||
|
||||
export function hsvToHex(hsv: Hsv): string {
|
||||
const { r, g, b } = hsvToRgb(hsv);
|
||||
return rgbToHex(r, g, b);
|
||||
}
|
||||
|
||||
/** 채도·명도 영역 배경 (hue 고정) */
|
||||
export function svPanelBackground(hue: number): string {
|
||||
const pure = hsvToHex({ h: hue, s: 1, v: 1 });
|
||||
return `linear-gradient(to top, #000, transparent), linear-gradient(to right, #fff, ${pure})`;
|
||||
}
|
||||
|
||||
/** 색상(Hue) 슬라이더 배경 */
|
||||
export const HUE_SLIDER_BG =
|
||||
'linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%)';
|
||||
Reference in New Issue
Block a user