UNPKG

lightswind

Version:

A professionally designed animate react component library & templates market that brings together functionality, accessibility, and beautiful aesthetics for modern applications.

121 lines (120 loc) 4.99 kB
import { jsx as _jsx } from "react/jsx-runtime"; import { useEffect, useRef, useCallback } from 'react'; import { cn } from '../lib/utils'; const MagicLoader = ({ size = 200, particleCount = 1, speed = 1, hueRange = [0, 360], className }) => { const canvasRef = useRef(null); const animationRef = useRef(); const particlesRef = useRef([]); const tickRef = useRef(0); const globalAngleRef = useRef(0); const globalRotationRef = useRef(0); const createParticle = useCallback((centerX, centerY, tick, minSize) => { return { radius: 7, x: centerX + Math.cos(tick / 20) * minSize / 2, y: centerY + Math.sin(tick / 20) * minSize / 2, angle: globalRotationRef.current + globalAngleRef.current, speed: 0, accel: 0.01, decay: 0.01, life: 1 }; }, []); const stepParticle = useCallback((particle, index) => { particle.speed += particle.accel; particle.x += Math.cos(particle.angle) * particle.speed * speed; particle.y += Math.sin(particle.angle) * particle.speed * speed; particle.angle += Math.PI / 64; particle.accel *= 1.01; particle.life -= particle.decay; if (particle.life <= 0) { particlesRef.current.splice(index, 1); } }, [speed]); const drawParticle = useCallback((ctx, particle, index, tick) => { const hue = hueRange[0] + ((tick + (particle.life * 120)) % (hueRange[1] - hueRange[0])); ctx.fillStyle = ctx.strokeStyle = `hsla(${hue}, 100%, 60%, ${particle.life})`; // Draw line to previous particle ctx.beginPath(); if (particlesRef.current[index - 1]) { ctx.moveTo(particle.x, particle.y); ctx.lineTo(particlesRef.current[index - 1].x, particlesRef.current[index - 1].y); } ctx.stroke(); // Draw main particle circle ctx.beginPath(); ctx.arc(particle.x, particle.y, Math.max(0.001, particle.life * particle.radius), 0, Math.PI * 2); ctx.fill(); // Draw sparkle effects const sparkleSize = Math.random() * 1.25; const sparkleX = particle.x + ((Math.random() - 0.5) * 35) * particle.life; const sparkleY = particle.y + ((Math.random() - 0.5) * 35) * particle.life; ctx.fillRect(Math.floor(sparkleX), Math.floor(sparkleY), sparkleSize, sparkleSize); }, [hueRange]); const animate = useCallback(() => { const canvas = canvasRef.current; if (!canvas) return; const ctx = canvas.getContext('2d'); if (!ctx) return; const rect = canvas.getBoundingClientRect(); const centerX = rect.width / 2; const centerY = rect.height / 2; const minSize = Math.min(rect.width, rect.height) * 0.5; // Add new particles for (let i = 0; i < particleCount; i++) { particlesRef.current.push(createParticle(centerX, centerY, tickRef.current, minSize)); } // Update particles particlesRef.current.forEach((particle, index) => { stepParticle(particle, index); }); // Clear canvas ctx.clearRect(0, 0, canvas.width, canvas.height); // Draw particles particlesRef.current.forEach((particle, index) => { drawParticle(ctx, particle, index, tickRef.current); }); // Update global rotation globalRotationRef.current += Math.PI / 6 * speed; globalAngleRef.current += Math.PI / 6 * speed; tickRef.current++; animationRef.current = requestAnimationFrame(animate); }, [createParticle, stepParticle, drawParticle, particleCount, speed]); const setupCanvas = useCallback(() => { const canvas = canvasRef.current; if (!canvas) return; const ctx = canvas.getContext('2d'); if (!ctx) return; // Set canvas size const dpr = window.devicePixelRatio || 1; canvas.width = size * dpr; canvas.height = size * dpr; canvas.style.width = `${size}px`; canvas.style.height = `${size}px`; ctx.scale(dpr, dpr); ctx.globalCompositeOperation = 'lighter'; // Reset animation state particlesRef.current = []; tickRef.current = 0; globalAngleRef.current = 0; globalRotationRef.current = 0; }, [size]); useEffect(() => { setupCanvas(); animate(); return () => { if (animationRef.current) { cancelAnimationFrame(animationRef.current); } }; }, [setupCanvas, animate]); return (_jsx("div", { className: cn("flex items-center justify-center", className), children: _jsx("canvas", { ref: canvasRef, className: "max-w-full max-h-full", style: { width: size, height: size } }) })); }; export default MagicLoader;