| | import { useState, useRef, useCallback } from 'react'; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | export const useParticleEffects = (containerRef, isMobile) => { |
| | const [particles, setParticles] = useState([]); |
| | const [popParticles, setPopParticles] = useState([]); |
| | const particleId = useRef(0); |
| | const isComponentMounted = useRef(true); |
| | |
| | |
| | const handleCreateSparkleParticles = useCallback((x, y) => { |
| | const newParticles = createSparkleParticles(x, y, containerRef, isMobile, particleId); |
| | setParticles(prev => [...prev, ...newParticles]); |
| | setTimeout(() => { |
| | if (isComponentMounted.current) { |
| | setParticles(prev => prev.filter(p => !newParticles.find(np => np.id === p.id))); |
| | } |
| | }, 1000); |
| | }, [isMobile, containerRef]); |
| |
|
| | |
| | const handleCreatePopParticles = useCallback((x, y) => { |
| | const newParticles = createPopParticles(x, y, containerRef, isMobile, particleId); |
| | setPopParticles(prev => [...prev, ...newParticles]); |
| | setTimeout(() => { |
| | if (isComponentMounted.current) { |
| | setPopParticles(prev => prev.filter(p => !newParticles.find(np => np.id === p.id))); |
| | } |
| | }, 600); |
| | }, [isMobile, containerRef]); |
| | |
| | return { |
| | particles, |
| | popParticles, |
| | createSparkleParticles: handleCreateSparkleParticles, |
| | createPopParticles: handleCreatePopParticles, |
| | isComponentMounted |
| | }; |
| | }; |
| |
|
| | |
| | |
| | |
| | const ParticleEffects = ({ particles, popParticles }) => { |
| | return ( |
| | <div className="absolute inset-0 pointer-events-none overflow-hidden"> |
| | {/* Render sparkle particles */} |
| | {particles.map(particle => ( |
| | <div |
| | key={particle.id} |
| | className="particle" |
| | style={particle.style} |
| | /> |
| | ))} |
| | |
| | {/* Render pop particles */} |
| | {popParticles.map(particle => ( |
| | <div |
| | key={particle.id} |
| | className="pop-particle" |
| | style={particle.style} |
| | /> |
| | ))} |
| | </div> |
| | ); |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | export const createSparkleParticles = (x, y, containerRef, isMobile, particleId) => { |
| | const newParticles = []; |
| | const numParticles = isMobile ? 8 : 12; |
| | |
| | |
| | const containerRect = containerRef.current.getBoundingClientRect(); |
| | const relativeX = x - containerRect.left; |
| | const relativeY = y - containerRect.top; |
| | |
| | for (let i = 0; i < numParticles; i++) { |
| | const angle = (Math.PI * 2 * i) / numParticles; |
| | const velocity = 2 + Math.random() * 2; |
| | const size = isMobile ? 3 + Math.random() * 2 : 4 + Math.random() * 3; |
| | const tx = Math.cos(angle) * (30 + Math.random() * 20); |
| | const ty = Math.sin(angle) * (30 + Math.random() * 20); |
| | const rotation = Math.random() * 360; |
| | |
| | newParticles.push({ |
| | id: particleId.current++, |
| | x: relativeX, |
| | y: relativeY, |
| | size, |
| | style: { |
| | '--tx': `${tx}px`, |
| | '--ty': `${ty}px`, |
| | '--r': `${rotation}deg`, |
| | width: `${size}px`, |
| | height: `${size}px`, |
| | left: `${relativeX}px`, |
| | top: `${relativeY}px` |
| | } |
| | }); |
| | } |
| | |
| | return newParticles; |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | export const createPopParticles = (x, y, containerRef, isMobile, particleId) => { |
| | const newParticles = []; |
| | const numParticles = isMobile ? 6 : 8; |
| | |
| | |
| | const containerRect = containerRef.current.getBoundingClientRect(); |
| | const relativeX = x - containerRect.left; |
| | const relativeY = y - containerRect.top; |
| | |
| | for (let i = 0; i < numParticles; i++) { |
| | const angle = (Math.PI * 2 * i) / numParticles; |
| | const velocity = 3 + Math.random() * 2; |
| | const size = isMobile ? 4 + Math.random() * 3 : 6 + Math.random() * 4; |
| | const tx = Math.cos(angle) * (40 + Math.random() * 20); |
| | const ty = Math.sin(angle) * (40 + Math.random() * 20); |
| | const rotation = Math.random() * 360; |
| | |
| | newParticles.push({ |
| | id: particleId.current++, |
| | x: relativeX, |
| | y: relativeY, |
| | size, |
| | style: { |
| | '--tx': `${tx}px`, |
| | '--ty': `${ty}px`, |
| | '--r': `${rotation}deg`, |
| | width: `${size}px`, |
| | height: `${size}px`, |
| | left: `${relativeX}px`, |
| | top: `${relativeY}px` |
| | } |
| | }); |
| | } |
| | |
| | return newParticles; |
| | }; |
| |
|
| | export default ParticleEffects; |