File size: 3,214 Bytes
6fe3275
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112

import React, { useEffect, useRef } from 'react';

const Background: React.FC<{ theme: 'light' | 'dark' }> = ({ theme }) => {
  const canvasRef = useRef<HTMLCanvasElement>(null);

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

    let animationFrameId: number;
    let time = 0;

    const resize = () => {
      canvas.width = window.innerWidth;
      canvas.height = window.innerHeight;
    };
    window.addEventListener('resize', resize);
    resize();

    const points: { x: number; y: number; z: number }[] = [];
    const numPoints = 50; // Reduced count slightly
    const size = 300;

    // Create a cube of dots
    for (let i = 0; i < numPoints; i++) {
      points.push({
        x: (Math.random() - 0.5) * size,
        y: (Math.random() - 0.5) * size,
        z: (Math.random() - 0.5) * size,
      });
    }

    const draw = () => {
      // Varied, slower speed
      time += 0.001 + Math.sin(Date.now() * 0.0005) * 0.0005; 
      
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      
      const cx = canvas.width / 2;
      const cy = canvas.height / 2;

      // Much lower opacity for better contrast with content
      const color = theme === 'dark' ? 'rgba(255, 255, 255, 0.08)' : 'rgba(0, 0, 0, 0.05)';
      const connectionColor = theme === 'dark' ? 'rgba(255, 255, 255, 0.02)' : 'rgba(0, 0, 0, 0.02)';

      ctx.fillStyle = color;
      ctx.strokeStyle = connectionColor;

      // Rotate points
      const rotatedPoints = points.map(p => {
        // Rotate Y
        let x = p.x * Math.cos(time) - p.z * Math.sin(time);
        let z = p.x * Math.sin(time) + p.z * Math.cos(time);
        // Rotate X
        let y = p.y * Math.cos(time * 0.5) - z * Math.sin(time * 0.5);
        z = p.y * Math.sin(time * 0.5) + z * Math.cos(time * 0.5);

        // Perspective projection
        const scale = 800 / (800 + z);
        return {
          x: cx + x * scale,
          y: cy + y * scale,
          scale
        };
      });

      // Draw connections
      ctx.beginPath();
      for (let i = 0; i < rotatedPoints.length; i++) {
        for (let j = i + 1; j < rotatedPoints.length; j++) {
          const dx = rotatedPoints[i].x - rotatedPoints[j].x;
          const dy = rotatedPoints[i].y - rotatedPoints[j].y;
          const dist = Math.sqrt(dx * dx + dy * dy);
          if (dist < 120) {
            ctx.moveTo(rotatedPoints[i].x, rotatedPoints[i].y);
            ctx.lineTo(rotatedPoints[j].x, rotatedPoints[j].y);
          }
        }
      }
      ctx.stroke();

      // Draw points
      rotatedPoints.forEach(p => {
        ctx.beginPath();
        ctx.arc(p.x, p.y, 2 * p.scale, 0, Math.PI * 2);
        ctx.fill();
      });

      animationFrameId = requestAnimationFrame(draw);
    };

    draw();

    return () => {
      window.removeEventListener('resize', resize);
      cancelAnimationFrame(animationFrameId);
    };
  }, [theme]);

  return (
    <canvas 
      ref={canvasRef} 
      className="fixed top-0 left-0 w-full h-full -z-10 pointer-events-none transition-opacity duration-1000 opacity-20" 
    />
  );
};

export default Background;