102 lines
2.7 KiB
TypeScript
102 lines
2.7 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect, useState } from 'react'
|
|
|
|
export function CursorEffect() {
|
|
const [position, setPosition] = useState({ x: -100, y: -100 })
|
|
const [isHovering, setIsHovering] = useState(false)
|
|
const [isVisible, setIsVisible] = useState(false)
|
|
|
|
useEffect(() => {
|
|
const handleMouseMove = (e: MouseEvent) => {
|
|
setPosition({ x: e.clientX, y: e.clientY })
|
|
setIsVisible(true)
|
|
|
|
const target = e.target as HTMLElement
|
|
const isInteractive = target.closest('a, button, [role="button"], input, textarea')
|
|
setIsHovering(!!isInteractive)
|
|
}
|
|
|
|
const handleMouseLeave = () => {
|
|
setIsVisible(false)
|
|
}
|
|
|
|
window.addEventListener('mousemove', handleMouseMove)
|
|
document.addEventListener('mouseleave', handleMouseLeave)
|
|
|
|
return () => {
|
|
window.removeEventListener('mousemove', handleMouseMove)
|
|
document.removeEventListener('mouseleave', handleMouseLeave)
|
|
}
|
|
}, [])
|
|
|
|
if (!isVisible) return null
|
|
|
|
return (
|
|
<>
|
|
{/* Simple crosshair cursor */}
|
|
<div
|
|
className="pointer-events-none fixed z-[9999] mix-blend-difference"
|
|
style={{
|
|
left: position.x,
|
|
top: position.y,
|
|
transform: 'translate(-50%, -50%)',
|
|
}}
|
|
>
|
|
{/* Crosshair */}
|
|
<svg
|
|
width={isHovering ? 32 : 24}
|
|
height={isHovering ? 32 : 24}
|
|
viewBox="0 0 24 24"
|
|
className="transition-all duration-150"
|
|
style={{ opacity: 0.7 }}
|
|
>
|
|
{/* Horizontal line */}
|
|
<line
|
|
x1="4" y1="12" x2="10" y2="12"
|
|
stroke="currentColor"
|
|
strokeWidth="1.5"
|
|
className="text-blue-900"
|
|
/>
|
|
<line
|
|
x1="14" y1="12" x2="20" y2="12"
|
|
stroke="currentColor"
|
|
strokeWidth="1.5"
|
|
className="text-blue-900"
|
|
/>
|
|
{/* Vertical line */}
|
|
<line
|
|
x1="12" y1="4" x2="12" y2="10"
|
|
stroke="currentColor"
|
|
strokeWidth="1.5"
|
|
className="text-blue-900"
|
|
/>
|
|
<line
|
|
x1="12" y1="14" x2="12" y2="20"
|
|
stroke="currentColor"
|
|
strokeWidth="1.5"
|
|
className="text-blue-900"
|
|
/>
|
|
{/* Center dot */}
|
|
<circle
|
|
cx="12" cy="12" r="2"
|
|
fill="currentColor"
|
|
className="text-blue-900"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
|
|
{/* Subtle trailing dot */}
|
|
<div
|
|
className="pointer-events-none fixed z-[9998] w-1 h-1 rounded-full bg-blue-400/30"
|
|
style={{
|
|
left: position.x,
|
|
top: position.y,
|
|
transform: 'translate(-50%, -50%)',
|
|
transition: 'left 0.15s ease-out, top 0.15s ease-out',
|
|
}}
|
|
/>
|
|
</>
|
|
)
|
|
}
|