react-atmospheres
Version:
A lightweight wrapper to add seasonal, themed visual effects in your React app.
163 lines (157 loc) • 6.82 kB
JavaScript
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
;