handspew / components /ParticleEffects.js
Tina Tarighian
initial
065d164
import { useState, useRef, useCallback } from 'react';
/**
* Hook for managing particle effects
* @param {React.RefObject} containerRef - Reference to container element
* @param {boolean} isMobile - Whether the device is mobile
* @returns {Object} Particle effects state and functions
*/
export const useParticleEffects = (containerRef, isMobile) => {
const [particles, setParticles] = useState([]);
const [popParticles, setPopParticles] = useState([]);
const particleId = useRef(0);
const isComponentMounted = useRef(true);
// Handler for sparkle particles
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]);
// Handler for pop particles
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
};
};
/**
* Particle effects component
*/
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>
);
};
/**
* Create sparkle particles
* @param {number} x - X position
* @param {number} y - Y position
* @param {React.RefObject} containerRef - Reference to container element
* @param {boolean} isMobile - Whether the device is mobile
* @param {React.MutableRefObject} particleId - Reference to particle ID counter
* @returns {Array} - Array of particle objects
*/
export const createSparkleParticles = (x, y, containerRef, isMobile, particleId) => {
const newParticles = [];
const numParticles = isMobile ? 8 : 12;
// Get container position for relative positioning
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;
};
/**
* Create pop particles
* @param {number} x - X position
* @param {number} y - Y position
* @param {React.RefObject} containerRef - Reference to container element
* @param {boolean} isMobile - Whether the device is mobile
* @param {React.MutableRefObject} particleId - Reference to particle ID counter
* @returns {Array} - Array of particle objects
*/
export const createPopParticles = (x, y, containerRef, isMobile, particleId) => {
const newParticles = [];
const numParticles = isMobile ? 6 : 8;
// Get container position for relative positioning
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;