File size: 2,330 Bytes
bf04727
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
"use client";
import { useEffect, useRef } from "react";

interface WaveformVisualizerProps {
  analyserNode: AnalyserNode | null;
  isActive: boolean;
  height?: number;
  color?: string;
}

export default function WaveformVisualizer({
  analyserNode,
  isActive,
  height = 80,
  color = "#8B5CF6",
}: WaveformVisualizerProps) {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const animFrameRef = useRef<number>(0);

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    if (!ctx) return;

    const draw = () => {
      animFrameRef.current = requestAnimationFrame(draw);
      const W = canvas.width;
      const H = canvas.height;
      ctx.clearRect(0, 0, W, H);

      if (!analyserNode || !isActive) {
        // Idle animation — gentle sine wave
        ctx.beginPath();
        const t = Date.now() / 800;
        for (let x = 0; x < W; x++) {
          const y = H / 2 + Math.sin(x * 0.03 + t) * 8 * Math.sin(x * 0.008 + t * 0.5);
          x === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
        }
        ctx.strokeStyle = color + "60";
        ctx.lineWidth = 2;
        ctx.stroke();
        return;
      }

      const bufLen = analyserNode.frequencyBinCount;
      const dataArray = new Uint8Array(bufLen);
      analyserNode.getByteTimeDomainData(dataArray);

      // Draw gradient waveform
      const gradient = ctx.createLinearGradient(0, 0, W, 0);
      gradient.addColorStop(0, "#8B5CF6");
      gradient.addColorStop(0.5, "#14b8a6");
      gradient.addColorStop(1, "#8B5CF6");

      ctx.beginPath();
      const sliceW = W / bufLen;
      let x = 0;
      for (let i = 0; i < bufLen; i++) {
        const v = dataArray[i] / 128.0;
        const y = (v * H) / 2;
        i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
        x += sliceW;
      }
      ctx.lineTo(W, H / 2);
      ctx.strokeStyle = gradient;
      ctx.lineWidth = 2.5;
      ctx.shadowBlur = 12;
      ctx.shadowColor = color;
      ctx.stroke();
      ctx.shadowBlur = 0;
    };

    draw();
    return () => cancelAnimationFrame(animFrameRef.current);
  }, [analyserNode, isActive, color]);

  return (
    <canvas
      ref={canvasRef}
      width={600}
      height={height}
      className="w-full rounded-xl"
      style={{ height }}
    />
  );
}