Refactor AssistantPage and DebugDrawer components

- Remove debug mode state management from AssistantPage, simplifying the component structure.
- Update DebugDrawer to eliminate mode selection, focusing on voice interaction features.
- Enhance the VoiceVisualizer component with improved visual effects and responsiveness to audio input.
- Adjust styles and layout for better user experience in the debugging interface.
This commit is contained in:
Xin Wang
2026-06-09 15:28:24 +08:00
parent c64b7dcf99
commit 4f0f639e8f
2 changed files with 216 additions and 130 deletions

View File

@@ -150,8 +150,8 @@ export function VoiceVisualizer({
const cx = size / 2;
const cy = size / 2;
const orbR = size * 0.2;
const maxBar = size * 0.18;
const orbR = size * 0.18;
const maxBar = size * 0.15;
const freq = new Uint8Array(128);
let raf = 0;
@@ -201,38 +201,82 @@ export function VoiceVisualizer({
ctx.clearRect(0, 0, size, size);
const breathe = 0.5 + 0.5 * Math.sin(t * 1.4);
const pulse = 1 + energy * 0.12 + breathe * 0.02;
const pulse = 1 + energy * 0.2 + breathe * 0.035;
// 外层光晕
// 整体环境光,让频谱像悬浮在空间中。
const glow = ctx.createRadialGradient(
cx,
cy,
orbR * 0.3,
orbR * 0.1,
cx,
cy,
size * 0.5,
);
glow.addColorStop(0, rgba(sky, 0.18 + energy * 0.35));
glow.addColorStop(0.55, rgba(lav, 0.08 + energy * 0.18));
glow.addColorStop(0, rgba(sky, 0.24 + energy * 0.42));
glow.addColorStop(0.38, rgba(lav, 0.12 + energy * 0.2));
glow.addColorStop(0.72, rgba(rose, 0.04 + energy * 0.08));
glow.addColorStop(1, rgba(lav, 0));
ctx.fillStyle = glow;
ctx.fillRect(0, 0, size, size);
// 环形频谱柱
// 远景漂浮粒子,旋转速度很慢,提供空间纵深。
for (let i = 0; i < 18; i++) {
const phase = i * 2.399 + t * (0.035 + (i % 4) * 0.008);
const radius = size * (0.31 + (i % 6) * 0.026);
const drift = Math.sin(t * 0.45 + i * 1.7) * size * 0.018;
const x = cx + Math.cos(phase) * (radius + drift);
const y = cy + Math.sin(phase) * (radius + drift);
const color = cyclicColor(palette, i / 18);
const particleR = size * (0.004 + (i % 3) * 0.002);
ctx.beginPath();
ctx.arc(x, y, particleR, 0, Math.PI * 2);
ctx.fillStyle = rgba(color, 0.13 + energy * 0.28);
ctx.shadowColor = rgba(color, 0.55);
ctx.shadowBlur = 5 + energy * 10;
ctx.fill();
}
ctx.shadowBlur = 0;
// 两条不完整的轨道弧光,制造环绕与旋转感。
ctx.lineWidth = Math.max(0.8, size * 0.005);
ctx.lineCap = "round";
ctx.lineWidth = Math.max(2, size * 0.012);
const rotation = t * 0.15;
for (let i = 0; i < 2; i++) {
const radius = size * (0.32 + i * 0.07);
const start = t * (i === 0 ? 0.12 : -0.08) + i * Math.PI;
const arc = Math.PI * (0.55 + i * 0.18);
const orbit = ctx.createLinearGradient(
cx - radius,
cy,
cx + radius,
cy,
);
orbit.addColorStop(0, rgba(lav, 0));
orbit.addColorStop(0.5, rgba(i === 0 ? sky : rose, 0.22 + energy * 0.3));
orbit.addColorStop(1, rgba(lav, 0));
ctx.strokeStyle = orbit;
ctx.shadowColor = rgba(i === 0 ? sky : rose, 0.4);
ctx.shadowBlur = 7;
ctx.beginPath();
ctx.arc(cx, cy, radius, start, start + arc);
ctx.stroke();
}
ctx.shadowBlur = 0;
// 外层频谱环:更细、更轻,负责表现扩散。
const rotation = t * 0.12;
ctx.lineCap = "round";
ctx.lineWidth = Math.max(1.2, size * 0.007);
for (let i = 0; i < barCount; i++) {
const angle = (i / barCount) * Math.PI * 2 + rotation;
const v = smooth[i];
const r0 = orbR * pulse + size * 0.03;
const r1 = r0 + maxBar * v + 1.5;
const r0 = size * 0.285 + energy * size * 0.025;
const r1 = r0 + maxBar * (0.2 + v * 0.95);
const cos = Math.cos(angle);
const sin = Math.sin(angle);
const color = cyclicColor(palette, i / barCount);
ctx.strokeStyle = rgba(color, 0.55 + v * 0.4);
ctx.strokeStyle = rgba(color, 0.3 + v * 0.55);
ctx.shadowColor = rgba(color, 0.8);
ctx.shadowBlur = 6 + v * 16;
ctx.shadowBlur = 5 + v * 18;
ctx.beginPath();
ctx.moveTo(cx + cos * r0, cy + sin * r0);
ctx.lineTo(cx + cos * r1, cy + sin * r1);
@@ -240,16 +284,53 @@ export function VoiceVisualizer({
}
ctx.shadowBlur = 0;
// 中心柔光:随音量发亮,不画实体球
// 内层频谱环:反向旋转,形成更有机的声场边缘。
ctx.lineWidth = Math.max(1.5, size * 0.009);
for (let i = 0; i < barCount; i += 2) {
const angle = (i / barCount) * Math.PI * 2 - t * 0.09;
const v = smooth[(i + Math.floor(barCount / 3)) % barCount];
const r0 = orbR * pulse + size * 0.018;
const r1 = r0 + size * (0.018 + v * 0.055);
const cos = Math.cos(angle);
const sin = Math.sin(angle);
const color = cyclicColor(palette, 1 - i / barCount);
ctx.strokeStyle = rgba(color, 0.38 + v * 0.5);
ctx.beginPath();
ctx.moveTo(cx + cos * r0, cy + sin * r0);
ctx.lineTo(cx + cos * r1, cy + sin * r1);
ctx.stroke();
}
// 中心声场核心:深色边缘包住高亮中心,音量越高越通透。
const coreR = orbR * pulse;
const core = ctx.createRadialGradient(cx, cy, 0, cx, cy, coreR);
core.addColorStop(0, rgba(sky, 0.22 + energy * 0.4));
core.addColorStop(0.6, rgba(lav, 0.06 + energy * 0.15));
core.addColorStop(1, rgba(lav, 0));
core.addColorStop(0, rgba(sky, 0.42 + energy * 0.45));
core.addColorStop(0.28, rgba(lav, 0.2 + energy * 0.28));
core.addColorStop(0.68, rgba(rose, 0.08 + energy * 0.16));
core.addColorStop(1, rgba(lav, 0.01));
ctx.shadowColor = rgba(sky, 0.5 + energy * 0.35);
ctx.shadowBlur = 18 + energy * 35;
ctx.beginPath();
ctx.arc(cx, cy, coreR, 0, Math.PI * 2);
ctx.fillStyle = core;
ctx.fill();
ctx.shadowBlur = 0;
// 核心表面的柔和高光,让它不只是一个平面渐变。
const highlight = ctx.createRadialGradient(
cx - coreR * 0.28,
cy - coreR * 0.32,
0,
cx - coreR * 0.15,
cy - coreR * 0.18,
coreR * 0.85,
);
highlight.addColorStop(0, rgba(sky, 0.22 + energy * 0.22));
highlight.addColorStop(1, rgba(sky, 0));
ctx.beginPath();
ctx.arc(cx, cy, coreR * 0.96, 0, Math.PI * 2);
ctx.fillStyle = highlight;
ctx.fill();
raf = requestAnimationFrame(draw);
};