Files
ai-video-fullstack/frontend/src/lib/visualizer-palette.ts
Xin Wang b3fbfac5df Implement audio visualizers and refactor AssistantPage
- 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.
2026-06-09 16:28:45 +08:00

63 lines
2.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 可视化共用的配色工具:从设计 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),
};
}