Files
my-loves-calendar/components/FairyDust.tsx
drjones c373bbdab4 first commit
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-16 21:05:27 -08:00

76 lines
2.0 KiB
TypeScript

import React, { useEffect, useState } from 'react';
interface Point {
x: number;
y: number;
id: number;
size: number;
color: string;
velocity: { x: number; y: number };
life: number;
}
export const FairyDust: React.FC = () => {
const [points, setPoints] = useState<Point[]>([]);
useEffect(() => {
const handleMouseMove = (e: MouseEvent) => {
const colors = ['#FF00CC', '#3333FF', '#7000FF', '#FFFFFF', '#00FFFF'];
const newPoint: Point = {
x: e.clientX,
y: e.clientY,
id: Date.now() + Math.random(),
size: Math.random() * 4 + 2,
color: colors[Math.floor(Math.random() * colors.length)],
velocity: {
x: (Math.random() - 0.5) * 2,
y: (Math.random() - 0.5) * 2
},
life: 1.0
};
setPoints(prev => [...prev.slice(-40), newPoint]); // Limit trail length
};
window.addEventListener('mousemove', handleMouseMove);
return () => window.removeEventListener('mousemove', handleMouseMove);
}, []);
useEffect(() => {
const interval = setInterval(() => {
setPoints(prev => prev
.map(p => ({
...p,
x: p.x + p.velocity.x,
y: p.y + p.velocity.y,
life: p.life - 0.05
}))
.filter(p => p.life > 0)
);
}, 16); // 60fps
return () => clearInterval(interval);
}, []);
return (
<div className="fixed inset-0 pointer-events-none z-[9999]">
{points.map(point => (
<div
key={point.id}
style={{
position: 'absolute',
left: point.x,
top: point.y,
width: point.size,
height: point.size,
backgroundColor: point.color,
borderRadius: '50%',
opacity: point.life,
transform: `scale(${point.life})`,
boxShadow: `0 0 ${point.size * 2}px ${point.color}`,
transition: 'opacity 0.1s linear'
}}
/>
))}
</div>
);
};