paint-spark-banksy-website/components/cursor-effects.tsx

131 lines
3.6 KiB
TypeScript

"use client"
import type React from "react"
import { useEffect, useState } from "react"
interface Particle {
id: number
x: number
y: number
type: "sparkle" | "rainbow"
color?: string
vx?: number
vy?: number
angle?: number
}
export function CursorEffects() {
const [particles, setParticles] = useState<Particle[]>([])
useEffect(() => {
let particleId = 0
let lastTime = Date.now()
const handleMouseMove = (e: MouseEvent) => {
const now = Date.now()
// Throttle particle creation to every 50ms
if (now - lastTime < 50) return
lastTime = now
const newParticles: Particle[] = []
for (let i = 0; i < 3; i++) {
const angle = Math.random() * Math.PI * 2
const speed = 2 + Math.random() * 3
newParticles.push({
id: particleId++,
x: e.clientX,
y: e.clientY,
type: "sparkle",
color: [
"#ff0000",
"#ff9a00",
"#d0de21",
"#4fdc4a",
"#3fdad8",
"#2fc9e2",
"#1c7fee",
"#5f15f2",
"#ba0cf8",
"#fb07d9",
][Math.floor(Math.random() * 10)],
vx: Math.cos(angle) * speed,
vy: Math.sin(angle) * speed,
angle: angle,
})
}
for (let i = 0; i < 2; i++) {
const angle = Math.random() * Math.PI * 2
const speed = 3 + Math.random() * 2
newParticles.push({
id: particleId++,
x: e.clientX,
y: e.clientY,
type: "rainbow",
vx: Math.cos(angle) * speed,
vy: Math.sin(angle) * speed,
angle: angle,
})
}
setParticles((prev) => [...prev, ...newParticles])
// Remove old particles after animation
setTimeout(() => {
setParticles((prev) => prev.filter((p) => !newParticles.find((np) => np.id === p.id)))
}, 1000)
}
window.addEventListener("mousemove", handleMouseMove)
return () => window.removeEventListener("mousemove", handleMouseMove)
}, [])
return (
<div className="pointer-events-none fixed inset-0 z-50">
{particles.map((particle) => (
<div
key={particle.id}
className="absolute"
style={{
left: particle.x,
top: particle.y,
}}
>
{particle.type === "sparkle" ? (
<div
className="h-3 w-3 -translate-x-1/2 -translate-y-1/2"
style={
{
animation: "cursor-spark 0.8s ease-out forwards",
"--vx": particle.vx,
"--vy": particle.vy,
} as React.CSSProperties
}
>
<svg viewBox="0 0 24 24" fill={particle.color}>
<path d="M12 0L14.59 9.41L24 12L14.59 14.59L12 24L9.41 14.59L0 12L9.41 9.41L12 0Z" />
</svg>
</div>
) : (
<div
className="h-12 w-2 -translate-x-1/2 -translate-y-1/2 rounded-full"
style={
{
background:
"linear-gradient(to bottom, #ff0000, #ff9a00, #d0de21, #4fdc4a, #3fdad8, #2fc9e2, #1c7fee, #5f15f2, #ba0cf8, #fb07d9)",
animation: "cursor-arc 0.7s ease-out forwards",
transform: `rotate(${(particle.angle || 0) * (180 / Math.PI)}deg)`,
"--vx": particle.vx,
"--vy": particle.vy,
} as React.CSSProperties
}
/>
)}
</div>
))}
</div>
)
}