decolonize-time-website/components/cursor-warp.tsx

143 lines
5.2 KiB
TypeScript

"use client"
import { useEffect, useRef, useState } from "react"
export function CursorWarp() {
const cursorRef = useRef<HTMLDivElement>(null)
const trailRef = useRef<HTMLDivElement>(null)
const [mousePos, setMousePos] = useState({ x: 0, y: 0 })
const [isMoving, setIsMoving] = useState(false)
useEffect(() => {
let timeout: NodeJS.Timeout
const handleMouseMove = (e: MouseEvent) => {
setMousePos({ x: e.clientX, y: e.clientY })
setIsMoving(true)
const elements = document.querySelectorAll(".warp-element")
elements.forEach((el) => {
const rect = el.getBoundingClientRect()
const centerX = rect.left + rect.width / 2
const centerY = rect.top + rect.height / 2
const dx = e.clientX - centerX
const dy = e.clientY - centerY
const distance = Math.sqrt(dx * dx + dy * dy)
// Black hole effect with stronger pull and rotation
if (distance < 300) {
const strength = Math.pow((300 - distance) / 300, 2) // Quadratic falloff for stronger pull
// Pull toward cursor (negative direction)
const pullX = -(dx / distance) * strength * 40
const pullY = -(dy / distance) * strength * 40
// Calculate angle for rotation around cursor
const angle = Math.atan2(dy, dx)
const rotationStrength = strength * 15 // Degrees of twist
// Perpendicular vector for tangential twist
const twistX = -Math.sin(angle) * strength * 15
const twistY = Math.cos(angle) * strength * 15
// Combine pull + twist + rotation
const totalX = pullX + twistX
const totalY = pullY + twistY
// Scale down as it gets pulled in (singularity effect)
const scale = 1 - strength * 0.3
;(el as HTMLElement).style.transform =
`translate(${totalX}px, ${totalY}px) scale(${scale}) rotate(${rotationStrength}deg)`
;(el as HTMLElement).style.filter = `blur(${strength * 2}px) brightness(${1 - strength * 0.3})`
} else {
;(el as HTMLElement).style.transform = ""
;(el as HTMLElement).style.filter = ""
}
})
clearTimeout(timeout)
timeout = setTimeout(() => setIsMoving(false), 100)
}
window.addEventListener("mousemove", handleMouseMove)
return () => {
window.removeEventListener("mousemove", handleMouseMove)
clearTimeout(timeout)
}
}, [])
return (
<>
{/* Custom cursor - black hole singularity */}
<div
ref={cursorRef}
className="fixed pointer-events-none z-50 mix-blend-difference"
style={{
left: `${mousePos.x}px`,
top: `${mousePos.y}px`,
transform: "translate(-50%, -50%)",
}}
>
<div className="relative">
<div className="w-3 h-3 bg-black border-2 border-white rounded-full animate-pulse" />
{/* Accretion disk effect */}
<div className="absolute inset-0 -translate-x-1/2 -translate-y-1/2">
<div
className="w-16 h-16 border border-white/30 rounded-full animate-spin"
style={{ animationDuration: "1s" }}
/>
</div>
<div className="absolute inset-0 -translate-x-1/2 -translate-y-1/2">
<div
className="w-24 h-24 border border-cyan-400/20 rounded-full animate-spin"
style={{ animationDuration: "1.5s", animationDirection: "reverse" }}
/>
</div>
{/* Orbiting matter being pulled in */}
<div className="absolute inset-0 animate-spin" style={{ animationDuration: "0.8s" }}>
<div className="absolute top-1/2 left-1/2 w-1.5 h-1.5 bg-cyan-400 rounded-full -translate-x-1/2 -translate-y-10 blur-sm" />
</div>
<div
className="absolute inset-0 animate-spin"
style={{ animationDuration: "1.2s", animationDirection: "reverse" }}
>
<div className="absolute top-1/2 left-1/2 w-1 h-1 bg-magenta-400 rounded-full -translate-x-1/2 -translate-y-14 blur-sm" />
</div>
{/* Gravitational wave ripples on movement */}
{isMoving && (
<>
<div className="absolute inset-0 -translate-x-1/2 -translate-y-1/2">
<div className="w-32 h-32 border-2 border-primary/40 rounded-full animate-[ripple_0.8s_ease-out]" />
</div>
<div className="absolute inset-0 -translate-x-1/2 -translate-y-1/2">
<div
className="w-40 h-40 border border-accent/30 rounded-full animate-[ripple_1s_ease-out]"
style={{ animationDelay: "0.1s" }}
/>
</div>
</>
)}
</div>
</div>
<div
ref={trailRef}
className="fixed pointer-events-none z-40"
style={{
left: `${mousePos.x}px`,
top: `${mousePos.y}px`,
transform: "translate(-50%, -50%)",
transition: "all 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94)",
}}
>
<div className="w-12 h-12 bg-gradient-radial from-primary/30 via-accent/20 to-transparent rounded-full blur-2xl animate-pulse" />
</div>
</>
)
}