| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283 |
- import { SpectrumRenderer, buildSpectrumPalette, hslToHex, rgbaFromHex } from './SpectrumRenderer'
- /**
- * 圆形频谱渲染器
- * 环形排列的放射线,缓慢旋转,彩虹色环效果
- */
- export class CircleSpectrumRenderer implements SpectrumRenderer {
- private rotation: number = 0
- render(ctx: CanvasRenderingContext2D, bands: number[], energy: number,
- width: number, height: number, brandColor: string): void {
- const bandCount: number = bands.length
- if (bandCount === 0) return
- ctx.fillStyle = 'rgba(0, 0, 0, 0.20)'
- ctx.fillRect(0, 0, width, height)
- const centerX: number = width / 2
- const centerY: number = height / 2
- const innerRadius: number = Math.min(width, height) * 0.08
- const maxOuterRadius: number = Math.min(width, height) * 0.48
- const palette = buildSpectrumPalette(brandColor)
- // 旋转速度随能量变化 (0.5x ~ 1.5x)
- const rotationSpeed: number = 0.5 + energy * 1.0
- this.rotation += rotationSpeed * 0.02 // ~10s 一圈基础速度
- if (this.rotation > Math.PI * 2) {
- this.rotation -= Math.PI * 2
- }
- const angleStep: number = (Math.PI * 2) / bandCount
- // 中央光晕
- const coreGradient: CanvasGradient = ctx.createRadialGradient(centerX, centerY, 0, centerX, centerY, maxOuterRadius * 0.7)
- coreGradient.addColorStop(0, rgbaFromHex(hslToHex(palette.primaryHue, 0.9, 0.55), 0.28 + energy * 0.25))
- coreGradient.addColorStop(1, 'rgba(0, 0, 0, 0)')
- ctx.fillStyle = coreGradient
- ctx.beginPath()
- ctx.arc(centerX, centerY, maxOuterRadius * 0.7, 0, Math.PI * 2)
- ctx.fill()
- for (let i = 0; i < bandCount; i++) {
- const angle: number = i * angleStep + this.rotation
- const lineLength: number = innerRadius + Math.pow(bands[i], 0.85) * (maxOuterRadius - innerRadius)
- const x1: number = centerX + Math.cos(angle) * innerRadius
- const y1: number = centerY + Math.sin(angle) * innerRadius
- const x2: number = centerX + Math.cos(angle) * lineLength
- const y2: number = centerY + Math.sin(angle) * lineLength
- // 品牌色驱动的色相映射
- const hue: number = (palette.primaryHue + (i / bandCount) * 220) % 360
- const lightness: number = 0.4 + bands[i] * 0.3
- const lineColor: string = hslToHex(hue, 0.85, lightness)
- ctx.beginPath()
- ctx.strokeStyle = rgbaFromHex(lineColor, 0.76)
- ctx.lineWidth = 1 + bands[i] * 2.6
- ctx.moveTo(x1, y1)
- ctx.lineTo(x2, y2)
- ctx.stroke()
- }
- // 外环脉冲
- const pulseRadius: number = innerRadius + (maxOuterRadius - innerRadius) * (0.35 + energy * 0.55)
- ctx.beginPath()
- ctx.arc(centerX, centerY, pulseRadius, 0, Math.PI * 2)
- ctx.strokeStyle = rgbaFromHex(hslToHex(palette.complementaryHue, 0.85, 0.62), 0.35)
- ctx.lineWidth = 2
- ctx.stroke()
- // 中心圆(能量呼吸)
- const breathRadius: number = innerRadius * (0.9 + energy * 0.8)
- ctx.beginPath()
- ctx.arc(centerX, centerY, breathRadius, 0, Math.PI * 2)
- ctx.fillStyle = rgbaFromHex(hslToHex(palette.coolHue, 0.7, 0.6), 0.18 + energy * 0.24)
- ctx.fill()
- }
- reset(): void {
- this.rotation = 0
- }
- }
|