| | 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; |
| |
|