| import React, { useEffect, useRef } from 'react'; |
|
|
| const BackgroundAnimation: React.FC = () => { |
| const canvasRef = useRef<HTMLCanvasElement>(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; |
|
|
| |
| const numStars = 400; |
| const speed = 2; |
| const stars: { x: number; y: number; z: number; o: number }[] = []; |
|
|
| |
| 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, |
| o: Math.random(), |
| }); |
| } |
|
|
| const animate = () => { |
| |
| ctx.fillStyle = '#020617'; |
| ctx.fillRect(0, 0, width, height); |
|
|
| const cx = width / 2; |
| const cy = height / 2; |
|
|
| stars.forEach((star) => { |
| |
| star.z -= speed; |
|
|
| |
| if (star.z <= 0) { |
| star.z = width; |
| star.x = Math.random() * width - width / 2; |
| star.y = Math.random() * height - height / 2; |
| } |
|
|
| |
| |
| const x = cx + (star.x / star.z) * width; |
| const y = cy + (star.y / star.z) * width; |
|
|
| |
| const size = (1 - star.z / width) * 3; |
|
|
| |
| const opacity = (1 - star.z / width); |
|
|
| |
| 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 ( |
| <div className="fixed inset-0 overflow-hidden pointer-events-none z-0 bg-slate-950"> |
| <canvas ref={canvasRef} className="absolute inset-0" /> |
| |
| {/* Subtle Nebula Overlay for atmosphere */} |
| <div className="absolute top-0 left-1/4 w-[600px] h-[600px] bg-blue-600/10 rounded-full mix-blend-screen filter blur-[120px] opacity-30 animate-blob" /> |
| <div className="absolute bottom-0 right-1/4 w-[600px] h-[600px] bg-purple-600/10 rounded-full mix-blend-screen filter blur-[120px] opacity-30 animate-blob animation-delay-2000" /> |
| </div> |
| ); |
| }; |
|
|
| export default BackgroundAnimation; |
|
|