Spaces:
Sleeping
Sleeping
File size: 3,432 Bytes
42ccbd9 |
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 106 107 108 109 110 111 112 113 114 115 |
import React, { useEffect, useRef } from 'react';
interface Particle {
x: number;
y: number;
vx: number;
vy: number;
size: number;
opacity: number;
color: string;
}
export function ParticleBackground() {
const canvasRef = useRef<HTMLCanvasElement>(null);
const particlesRef = useRef<Particle[]>([]);
const animationRef = useRef<number>();
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext('2d');
if (!ctx) return;
const resizeCanvas = () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
};
const createParticles = () => {
particlesRef.current = [];
const particleCount = Math.min(50, Math.floor((canvas.width * canvas.height) / 15000));
for (let i = 0; i < particleCount; i++) {
particlesRef.current.push({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
vx: (Math.random() - 0.5) * 0.8,
vy: (Math.random() - 0.5) * 0.8,
size: Math.random() * 3 + 1,
opacity: Math.random() * 0.5 + 0.1,
color: Math.random() > 0.5 ? '#3B82F6' : '#14B8A6'
});
}
};
const animate = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
particlesRef.current.forEach((particle, index) => {
// Update position
particle.x += particle.vx;
particle.y += particle.vy;
// Bounce off edges
if (particle.x < 0 || particle.x > canvas.width) particle.vx *= -1;
if (particle.y < 0 || particle.y > canvas.height) particle.vy *= -1;
// Keep particles in bounds
particle.x = Math.max(0, Math.min(canvas.width, particle.x));
particle.y = Math.max(0, Math.min(canvas.height, particle.y));
// Draw particle
ctx.beginPath();
ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2);
ctx.fillStyle = `${particle.color}${Math.floor(particle.opacity * 255).toString(16).padStart(2, '0')}`;
ctx.fill();
// Draw connections
particlesRef.current.slice(index + 1).forEach(otherParticle => {
const dx = particle.x - otherParticle.x;
const dy = particle.y - otherParticle.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 100) {
const opacity = (1 - distance / 100) * 0.2;
ctx.beginPath();
ctx.moveTo(particle.x, particle.y);
ctx.lineTo(otherParticle.x, otherParticle.y);
ctx.strokeStyle = `#60A5FA${Math.floor(opacity * 255).toString(16).padStart(2, '0')}`;
ctx.lineWidth = 1;
ctx.stroke();
}
});
});
animationRef.current = requestAnimationFrame(animate);
};
resizeCanvas();
createParticles();
animate();
const handleResize = () => {
resizeCanvas();
createParticles();
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
if (animationRef.current) {
cancelAnimationFrame(animationRef.current);
}
};
}, []);
return (
<canvas
ref={canvasRef}
className="absolute inset-0 w-full h-full pointer-events-none opacity-60 dark:opacity-40"
/>
);
} |