- Introduce three new audio visualizer components: AuraVisualizer, SpectrumVisualizer, and WaveVisualizer, enhancing the audio interaction experience. - Replace the deprecated VoiceVisualizer with the new visualizers, ensuring a cohesive visual language across components. - Update the AssistantPage to support dynamic visualization style switching, improving user engagement during audio interactions. - Refactor DebugVoicePanel to accommodate the new visualizer props and enhance the overall debugging interface.
63 lines
2.0 KiB
TypeScript
63 lines
2.0 KiB
TypeScript
// 可视化共用的配色工具:从设计 token(--gradient-*)读取调色板,
|
||
// 让三种可视化样式自动跟随明暗主题,并保持同一套视觉语言。
|
||
|
||
export type RGB = { r: number; g: number; b: number };
|
||
|
||
export type Palette = { sky: RGB; lav: RGB; rose: RGB };
|
||
|
||
/** 浅色主题下的回退值,避免首帧 token 未就绪时闪烁 */
|
||
const FALLBACK: Palette = {
|
||
sky: { r: 168, g: 200, b: 232 },
|
||
lav: { r: 200, g: 184, b: 224 },
|
||
rose: { r: 232, g: 184, b: 196 },
|
||
};
|
||
|
||
export function parseHex(hex: string, fallback: RGB): RGB {
|
||
const m = hex.trim().replace("#", "");
|
||
if (m.length === 6) {
|
||
return {
|
||
r: parseInt(m.slice(0, 2), 16),
|
||
g: parseInt(m.slice(2, 4), 16),
|
||
b: parseInt(m.slice(4, 6), 16),
|
||
};
|
||
}
|
||
if (m.length === 3) {
|
||
return {
|
||
r: parseInt(m[0] + m[0], 16),
|
||
g: parseInt(m[1] + m[1], 16),
|
||
b: parseInt(m[2] + m[2], 16),
|
||
};
|
||
}
|
||
return fallback;
|
||
}
|
||
|
||
export function rgba({ r, g, b }: RGB, a: number) {
|
||
return `rgba(${r}, ${g}, ${b}, ${a})`;
|
||
}
|
||
|
||
export function mix(a: RGB, b: RGB, t: number): RGB {
|
||
return {
|
||
r: Math.round(a.r + (b.r - a.r) * t),
|
||
g: Math.round(a.g + (b.g - a.g) * t),
|
||
b: Math.round(a.b + (b.b - a.b) * t),
|
||
};
|
||
}
|
||
|
||
/** 在 sky → lavender → rose 三色之间取环形插值(首尾相连,适合沿圆周/横向铺色) */
|
||
export function cyclicColor({ sky, lav, rose }: Palette, p: number): RGB {
|
||
const stops = [sky, lav, rose];
|
||
const scaled = ((p % 1) + 1) % 1 * stops.length;
|
||
const i = Math.floor(scaled);
|
||
return mix(stops[i % stops.length], stops[(i + 1) % stops.length], scaled - i);
|
||
}
|
||
|
||
/** 从元素上读取当前主题的 --gradient-* token,返回 0~255 的 RGB */
|
||
export function readPalette(el: Element): Palette {
|
||
const s = getComputedStyle(el);
|
||
return {
|
||
sky: parseHex(s.getPropertyValue("--gradient-sky"), FALLBACK.sky),
|
||
lav: parseHex(s.getPropertyValue("--gradient-lavender"), FALLBACK.lav),
|
||
rose: parseHex(s.getPropertyValue("--gradient-rose"), FALLBACK.rose),
|
||
};
|
||
}
|