Push
This commit is contained in:
142
src/components/ParticleBackground.tsx
Normal file
142
src/components/ParticleBackground.tsx
Normal file
@@ -0,0 +1,142 @@
|
||||
"use client";
|
||||
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
|
||||
interface Particle {
|
||||
id: number;
|
||||
x: number;
|
||||
y: number;
|
||||
size: number;
|
||||
speedX: number;
|
||||
speedY: number;
|
||||
opacity: number;
|
||||
color: string;
|
||||
}
|
||||
|
||||
export const ParticleBackground: React.FC = () => {
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
|
||||
const particlesRef = useRef<Particle[]>([]);
|
||||
const animationRef = useRef<number | undefined>(undefined);
|
||||
const mouseRef = useRef({ x: 0, y: 0 });
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = () => {
|
||||
setDimensions({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight
|
||||
});
|
||||
};
|
||||
|
||||
const handleMouseMove = (e: MouseEvent) => {
|
||||
mouseRef.current = { x: e.clientX, y: e.clientY };
|
||||
};
|
||||
|
||||
handleResize();
|
||||
window.addEventListener('resize', handleResize);
|
||||
window.addEventListener('mousemove', handleMouseMove);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
window.removeEventListener('mousemove', handleMouseMove);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) return;
|
||||
|
||||
canvas.width = dimensions.width;
|
||||
canvas.height = dimensions.height;
|
||||
|
||||
// Initialize particles
|
||||
const particleCount = Math.floor((dimensions.width * dimensions.height) / 15000);
|
||||
particlesRef.current = Array.from({ length: particleCount }, (_, i) => ({
|
||||
id: i,
|
||||
x: Math.random() * dimensions.width,
|
||||
y: Math.random() * dimensions.height,
|
||||
size: Math.random() * 2 + 0.5,
|
||||
speedX: (Math.random() - 0.5) * 0.5,
|
||||
speedY: (Math.random() - 0.5) * 0.5,
|
||||
opacity: Math.random() * 0.5 + 0.2,
|
||||
color: Math.random() > 0.7 ? '#FFAA00' : '#00D4FF'
|
||||
}));
|
||||
|
||||
const animate = () => {
|
||||
ctx.clearRect(0, 0, dimensions.width, dimensions.height);
|
||||
|
||||
particlesRef.current.forEach((particle, index) => {
|
||||
// Update position
|
||||
particle.x += particle.speedX;
|
||||
particle.y += particle.speedY;
|
||||
|
||||
// Mouse interaction - particles move away from cursor
|
||||
const dx = mouseRef.current.x - particle.x;
|
||||
const dy = mouseRef.current.y - particle.y;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (distance < 100) {
|
||||
const force = (100 - distance) / 100;
|
||||
particle.x -= (dx / distance) * force * 2;
|
||||
particle.y -= (dy / distance) * force * 2;
|
||||
}
|
||||
|
||||
// Wrap around screen
|
||||
if (particle.x < 0) particle.x = dimensions.width;
|
||||
if (particle.x > dimensions.width) particle.x = 0;
|
||||
if (particle.y < 0) particle.y = dimensions.height;
|
||||
if (particle.y > dimensions.height) particle.y = 0;
|
||||
|
||||
// Draw particle
|
||||
ctx.beginPath();
|
||||
ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2);
|
||||
ctx.fillStyle = particle.color.replace(')', `, ${particle.opacity})`).replace('rgb', 'rgba').replace('#', '');
|
||||
|
||||
// Convert hex to rgba
|
||||
const hex = particle.color;
|
||||
const r = parseInt(hex.slice(1, 3), 16);
|
||||
const g = parseInt(hex.slice(3, 5), 16);
|
||||
const b = parseInt(hex.slice(5, 7), 16);
|
||||
ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${particle.opacity})`;
|
||||
ctx.fill();
|
||||
|
||||
// Draw connections between nearby particles
|
||||
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 < 120) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(particle.x, particle.y);
|
||||
ctx.lineTo(otherParticle.x, otherParticle.y);
|
||||
ctx.strokeStyle = `rgba(0, 212, 255, ${0.1 * (1 - distance / 120)})`;
|
||||
ctx.lineWidth = 0.5;
|
||||
ctx.stroke();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
animationRef.current = requestAnimationFrame(animate);
|
||||
};
|
||||
|
||||
animate();
|
||||
|
||||
return () => {
|
||||
if (animationRef.current) {
|
||||
cancelAnimationFrame(animationRef.current);
|
||||
}
|
||||
};
|
||||
}, [dimensions]);
|
||||
|
||||
return (
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
className="fixed inset-0 pointer-events-none z-0"
|
||||
style={{ background: 'transparent' }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user