import { SpectrumRenderer, SpectrumRenderOptions, buildSpectrumPalette, hslToHex, rgbaFromHex } from './SpectrumRenderer' /** * 星爆频谱渲染器 * 从中心向外爆发的星芒线条 */ export class StarburstSpectrumRenderer implements SpectrumRenderer { private drift: number = 0 render(ctx: CanvasRenderingContext2D, bands: number[], energy: number, width: number, height: number, brandColor: string, options?: SpectrumRenderOptions): void { if (bands.length === 0) return ctx.clearRect(0, 0, width, height) const palette = buildSpectrumPalette(brandColor) const centerX: number = width / 2 const centerY: number = height / 2 const complexityScale: number = Math.max(0.45, Math.min(1, options?.complexityScale ?? 1)) const rays: number = Math.max(18, Math.floor(44 * complexityScale)) const outerMax: number = Math.min(width, height) * 0.46 const innerRadius: number = Math.min(width, height) * 0.08 this.drift += 0.015 + energy * 0.01 for (let i = 0; i < rays; i++) { const bandIndex: number = Math.floor((i / rays) * bands.length) const value: number = Math.pow(bands[bandIndex], 0.8) const angle: number = (Math.PI * 2 * i) / rays + this.drift const len: number = innerRadius + value * (outerMax - innerRadius) const x1: number = centerX + Math.cos(angle) * innerRadius const y1: number = centerY + Math.sin(angle) * innerRadius const x2: number = centerX + Math.cos(angle) * len const y2: number = centerY + Math.sin(angle) * len const hue: number = (palette.warmHue + i * 5) % 360 ctx.beginPath() ctx.moveTo(x1, y1) ctx.lineTo(x2, y2) ctx.strokeStyle = rgbaFromHex(hslToHex(hue, 0.88, 0.58), 0.20 + value * 0.70) ctx.lineWidth = 1 + value * 2.2 ctx.stroke() } const coreRadius: number = innerRadius * (0.85 + energy * 0.65) ctx.beginPath() ctx.arc(centerX, centerY, coreRadius, 0, Math.PI * 2) ctx.fillStyle = rgbaFromHex(hslToHex(palette.complementaryHue, 0.82, 0.62), 0.14 + energy * 0.18) ctx.fill() } reset(): void { this.drift = 0 } }