File size: 5,249 Bytes
065d164 |
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 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
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; |