📄 Starfield.js
/home/palash/git/site/node_modules/new_site/src/components/Starfield.js
Language: js • Lines: 118
import { useEffect, useRef } from "react";

export default function Starfield() {
  const canvasRef = useRef(null);

  useEffect(() => {
    const canvas = canvasRef.current;
    const ctx = canvas.getContext("2d");
    let animationId;

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

    const starColors = [
      [255, 255, 255],   // white
      [186, 230, 253],   // light blue
      [253, 224, 171],   // warm gold
      [209, 250, 229],   // soft green
      [243, 232, 255],   // soft purple
      [254, 205, 211],   // soft pink
    ];

    const orbs = [
      { x: 0.15, y: 0.3, radius: 350, color: [34, 211, 238], speed: 0.0006, phase: 0 },
      { x: 0.75, y: 0.6, radius: 300, color: [139, 92, 246], speed: 0.0004, phase: 2 },
      { x: 0.5, y: 0.2, radius: 250, color: [245, 158, 11], speed: 0.0007, phase: 4 },
    ];

    const stars = [];
    const createStars = () => {
      stars.length = 0;
      const count = Math.floor((canvas.width * canvas.height) / 5000);
      for (let i = 0; i < count; i++) {
        const color = starColors[Math.floor(Math.random() * starColors.length)];
        const size = Math.random() * 1.8 + 0.3;
        stars.push({
          x: Math.random() * canvas.width,
          y: Math.random() * canvas.height,
          size,
          color,
          opacity: Math.random() * 0.4 + 0.15,
          driftX: (Math.random() - 0.5) * 0.08,
          driftY: (Math.random() - 0.5) * 0.05,
        });
      }
    };

    const animate = (time) => {
      ctx.clearRect(0, 0, canvas.width, canvas.height);

      orbs.forEach((orb) => {
        const cx = canvas.width * (orb.x + Math.sin(time * orb.speed + orb.phase) * 0.04);
        const cy = canvas.height * (orb.y + Math.cos(time * orb.speed * 0.7 + orb.phase) * 0.03);
        const gradient = ctx.createRadialGradient(cx, cy, 0, cx, cy, orb.radius);
        gradient.addColorStop(0, `rgba(${orb.color.join(",")}, 0.025)`);
        gradient.addColorStop(1, "transparent");
        ctx.fillStyle = gradient;
        ctx.fillRect(0, 0, canvas.width, canvas.height);
      });

      stars.forEach((star) => {
        star.x += star.driftX;
        star.y += star.driftY;

        if (star.x < -10) star.x = canvas.width + 10;
        if (star.x > canvas.width + 10) star.x = -10;
        if (star.y < -10) star.y = canvas.height + 10;
        if (star.y > canvas.height + 10) star.y = -10;

        const [r, g, b] = star.color;

        const gradient = ctx.createRadialGradient(
          star.x - star.size * 0.3, star.y - star.size * 0.3, 0,
          star.x, star.y, star.size
        );
        gradient.addColorStop(0, `rgba(255, 255, 255, ${star.opacity * 0.8})`);
        gradient.addColorStop(0.4, `rgba(${r}, ${g}, ${b}, ${star.opacity})`);
        gradient.addColorStop(1, `rgba(${r}, ${g}, ${b}, ${star.opacity * 0.3})`);

        ctx.beginPath();
        ctx.arc(star.x, star.y, star.size, 0, Math.PI * 2);
        ctx.fillStyle = gradient;
        ctx.fill();
      });

      animationId = requestAnimationFrame(animate);
    };

    resize();
    createStars();
    animationId = requestAnimationFrame(animate);

    const handleResize = () => { resize(); createStars(); };
    window.addEventListener("resize", handleResize);

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

  return (
    <canvas
      ref={canvasRef}
      style={{
        position: "fixed",
        top: 0,
        left: 0,
        width: "100%",
        height: "100%",
        zIndex: -1,
        pointerEvents: "none",
      }}
    />
  );
}