UNPKG

react-atmospheres

Version:

A lightweight wrapper to add seasonal, themed visual effects in your React app.

163 lines (157 loc) 6.82 kB
'use strict'; var jsxRuntime = require('react/jsx-runtime'); var react = require('react'); const SnowAnimation = ({ containerRef, color = 'rgba(200,200,200,0.8)', outlineColor = 'rgba(0,0,0,0.1)', maxFlakes = 50, speedFactor = 1, }) => { const canvasRef = react.useRef(null); react.useEffect(() => { const container = containerRef.current; const canvas = canvasRef.current; if (!canvas || !container) return; const ctx = canvas.getContext('2d'); if (!ctx) return; const resize = () => { canvas.width = container.clientWidth; canvas.height = container.clientHeight; }; resize(); const snowflakes = Array.from({ length: maxFlakes }).map(() => ({ x: Math.random() * canvas.width, y: Math.random() * canvas.height, r: Math.random() * 4 + 1, d: Math.random() * maxFlakes, })); let angle = 0; let frameId; const draw = () => { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.shadowColor = 'rgba(0,0,0,0.2)'; ctx.shadowBlur = 2; snowflakes.forEach((flake) => { ctx.beginPath(); ctx.arc(flake.x, flake.y, flake.r, 0, Math.PI * 2); ctx.fillStyle = color; ctx.fill(); ctx.strokeStyle = outlineColor; ctx.lineWidth = 0.5; ctx.stroke(); }); angle += 0.01 * speedFactor; snowflakes.forEach((flake) => { flake.y += Math.cos(angle + flake.d) + 1 + (flake.r / 2) * speedFactor; flake.x += Math.sin(angle) * 2 * speedFactor; if (flake.y > canvas.height) { flake.y = 0; flake.x = Math.random() * canvas.width; } }); frameId = requestAnimationFrame(draw); }; window.addEventListener('resize', resize); draw(); return () => { cancelAnimationFrame(frameId); window.removeEventListener('resize', resize); }; }, [containerRef, color, outlineColor, maxFlakes, speedFactor]); return (jsxRuntime.jsx("canvas", { ref: canvasRef, style: { position: 'absolute', top: 0, left: 0, pointerEvents: 'none', }, "aria-hidden": "true" })); }; const HeartAnimation = ({ containerRef, color = 'rgba(255,100,150,0.8)', outlineColor = 'rgba(200,50,100,0.5)', maxHearts = 50, speedFactor = 1, }) => { const canvasRef = react.useRef(null); const drawHeart = (ctx, x, y, size, fill, stroke, alpha) => { ctx.save(); ctx.globalAlpha = alpha; ctx.beginPath(); ctx.moveTo(x, y + size / 4); ctx.quadraticCurveTo(x, y, x + size / 4, y); ctx.quadraticCurveTo(x + size / 2, y, x + size / 2, y + size / 4); ctx.quadraticCurveTo(x + size / 2, y + size / 2, x + size / 4, y + (size * 3) / 4); ctx.lineTo(x, y + size); ctx.lineTo(x - size / 4, y + (size * 3) / 4); ctx.quadraticCurveTo(x - size / 2, y + size / 2, x - size / 2, y + size / 4); ctx.quadraticCurveTo(x - size / 2, y, x - size / 4, y); ctx.quadraticCurveTo(x, y, x, y + size / 4); ctx.closePath(); ctx.fillStyle = fill; ctx.fill(); ctx.lineWidth = 1; ctx.strokeStyle = stroke; ctx.stroke(); ctx.restore(); }; react.useEffect(() => { const canvas = canvasRef.current; const container = containerRef.current; if (!canvas || !container) return; const ctx = canvas.getContext('2d'); if (!ctx) return; const resize = () => { canvas.width = container.clientWidth; canvas.height = container.clientHeight; }; resize(); window.addEventListener('resize', resize); const hearts = Array.from({ length: maxHearts }).map(() => ({ x: Math.random() * canvas.width, y: canvas.height + Math.random() * 100, size: Math.random() * 20 + 10, speed: Math.random() + 0.5 * speedFactor, alpha: Math.random() * 0.5 + 0.5, drift: (Math.random() - 0.5) * 0.5, })); let frameId; const animate = () => { ctx.clearRect(0, 0, canvas.width, canvas.height); hearts.forEach((h) => { drawHeart(ctx, h.x, h.y, h.size, color, outlineColor, h.alpha); h.y -= h.speed; h.x += h.drift; h.alpha -= 0.002; if (h.alpha <= 0 || h.y < -h.size) { h.x = Math.random() * canvas.width; h.y = canvas.height + Math.random() * 50; h.size = Math.random() * 20 + 10; h.speed = Math.random() + 0.5 * speedFactor; h.alpha = Math.random() * 0.5 + 0.5; h.drift = (Math.random() - 0.5) * 0.5; } }); frameId = requestAnimationFrame(animate); }; animate(); return () => { cancelAnimationFrame(frameId); window.removeEventListener('resize', resize); }; }, [containerRef, color, outlineColor, maxHearts, speedFactor]); return (jsxRuntime.jsx("canvas", { ref: canvasRef, style: { position: 'absolute', top: 0, left: 0, pointerEvents: 'none', }, "aria-hidden": "true" })); }; const Atmospheres = ({ children, animation = 'none', disabled = false, particleAmount, color, outlineColor, speedFactor = 1, }) => { const containerRef = react.useRef(null); react.useEffect(() => { if (particleAmount && particleAmount > 50) { particleAmount = 50; } if (speedFactor && speedFactor > 2) { speedFactor = 2; } }, [particleAmount]); return (jsxRuntime.jsxs("div", { style: { position: 'relative' }, children: [jsxRuntime.jsx("div", { ref: containerRef, children: children }), !disabled && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [animation === 'snow' && (jsxRuntime.jsx(SnowAnimation, { color: color, containerRef: containerRef, outlineColor: outlineColor, maxFlakes: particleAmount, speedFactor: speedFactor })), animation === 'hearts' && (jsxRuntime.jsx(HeartAnimation, { color: color, containerRef: containerRef, outlineColor: outlineColor, maxHearts: particleAmount, speedFactor: speedFactor }))] }))] })); }; exports.Atmospheres = Atmospheres; exports.HeartAnimation = HeartAnimation; exports.SnowAnimation = SnowAnimation; //# sourceMappingURL=index.cjs.js.map