File size: 2,923 Bytes
21c7db9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import { useEffect, useRef } from "react";

type Star = {
  x: number;
  y: number;
  z: number;
  size: number;
  speed: number;
};

function createStars(count: number): Star[] {
  return Array.from({ length: count }, () => ({
    x: Math.random() * 2 - 1,
    y: Math.random() * 2 - 1,
    z: Math.random(),
    size: Math.random() * 1.4 + 0.25,
    speed: Math.random() * 0.00055 + 0.00018,
  }));
}

function StarCanvas() {
  const canvasRef = useRef<HTMLCanvasElement | null>(null);

  useEffect(() => {
    const canvas = canvasRef.current;
    const context = canvas?.getContext("2d");
    if (!canvas || !context) return undefined;

    let animationFrame = 0;
    let width = 0;
    let height = 0;
    let centerX = 0;
    let centerY = 0;
    const stars = createStars(680);

    const resize = () => {
      const pixelRatio = Math.min(window.devicePixelRatio || 1, 2);
      width = window.innerWidth;
      height = window.innerHeight;
      centerX = width / 2;
      centerY = height / 2;
      canvas.width = Math.floor(width * pixelRatio);
      canvas.height = Math.floor(height * pixelRatio);
      canvas.style.width = `${width}px`;
      canvas.style.height = `${height}px`;
      context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
    };

    const draw = () => {
      context.clearRect(0, 0, width, height);
      context.globalCompositeOperation = "lighter";

      stars.forEach((star) => {
        star.z -= star.speed;
        if (star.z <= 0.02) {
          star.x = Math.random() * 2 - 1;
          star.y = Math.random() * 2 - 1;
          star.z = 1;
        }

        const perspective = 1 / star.z;
        const x = centerX + star.x * perspective * centerX;
        const y = centerY + star.y * perspective * centerY;
        const opacity = Math.max(0, Math.min(1, 1.15 - star.z));
        const radius = star.size * perspective * 0.85;

        context.beginPath();
        context.fillStyle = `rgba(210, 246, 255, ${opacity})`;
        context.arc(x, y, radius, 0, Math.PI * 2);
        context.fill();
      });

      animationFrame = window.requestAnimationFrame(draw);
    };

    resize();
    draw();
    window.addEventListener("resize", resize);

    return () => {
      window.removeEventListener("resize", resize);
      window.cancelAnimationFrame(animationFrame);
    };
  }, []);

  return <canvas ref={canvasRef} />;
}

export default function MetaverseBackdrop() {
  return (
    <div className="metaverse-backdrop" aria-hidden="true">
      <video className="blackhole-video" autoPlay muted loop playsInline preload="auto">
        <source src="/blackhole.webm" type="video/webm" />
      </video>
      <div className="stars-canvas">
        <StarCanvas />
      </div>
      <div className="nebula-orb orb-one" />
      <div className="nebula-orb orb-two" />
      <div className="nebula-grid" />
      <div className="cosmic-vignette" />
    </div>
  );
}