import React, { useEffect, useRef } from 'react'; const BackgroundAnimation: React.FC = () => { const canvasRef = useRef(null); useEffect(() => { const canvas = canvasRef.current; if (!canvas) return; const ctx = canvas.getContext('2d'); if (!ctx) return; let width = window.innerWidth; let height = window.innerHeight; canvas.width = width; canvas.height = height; // Star parameters const numStars = 400; const speed = 2; // Speed of travel const stars: { x: number; y: number; z: number; o: number }[] = []; // Initialize stars for (let i = 0; i < numStars; i++) { stars.push({ x: Math.random() * width - width / 2, y: Math.random() * height - height / 2, z: Math.random() * width, // Depth o: Math.random(), // Original z for resetting }); } const animate = () => { // Clear screen with a slight fade trail for motion blur effect (optional, using clearRect for crispness now) ctx.fillStyle = '#020617'; // Match slate-950 ctx.fillRect(0, 0, width, height); const cx = width / 2; const cy = height / 2; stars.forEach((star) => { // Move star closer star.z -= speed; // Reset if it passes the screen if (star.z <= 0) { star.z = width; star.x = Math.random() * width - width / 2; star.y = Math.random() * height - height / 2; } // Project 3D to 2D // The factor 'width / star.z' makes things bigger as they get closer (z decreases) const x = cx + (star.x / star.z) * width; const y = cy + (star.y / star.z) * width; // Calculate size based on proximity const size = (1 - star.z / width) * 3; // Calculate opacity based on proximity (fade in as they appear) const opacity = (1 - star.z / width); // Draw star if (x >= 0 && x <= width && y >= 0 && y <= height) { ctx.beginPath(); ctx.fillStyle = `rgba(255, 255, 255, ${opacity})`; ctx.arc(x, y, size, 0, Math.PI * 2); ctx.fill(); } }); requestAnimationFrame(animate); }; const animationId = requestAnimationFrame(animate); const handleResize = () => { width = window.innerWidth; height = window.innerHeight; canvas.width = width; canvas.height = height; }; window.addEventListener('resize', handleResize); return () => { cancelAnimationFrame(animationId); window.removeEventListener('resize', handleResize); }; }, []); return (
{/* Subtle Nebula Overlay for atmosphere */}
); }; export default BackgroundAnimation;