'use client' import React, { useEffect, useRef } from 'react' interface GhostCursorProps { isActive: boolean containerRef: React.RefObject targetId?: string | null } export function GhostCursor({ isActive, containerRef, targetId }: GhostCursorProps) { const elRef = useRef(null) const rafRef = useRef(null) const posRef = useRef<{ x: number; y: number; visible: boolean }>({ x: 0, y: 0, visible: false }) const targetIdRef = useRef(targetId) targetIdRef.current = targetId const isActiveRef = useRef(isActive) isActiveRef.current = isActive useEffect(() => { if (!isActive) { posRef.current.visible = false if (elRef.current) elRef.current.style.opacity = '0' if (rafRef.current) cancelAnimationFrame(rafRef.current) return } let lastMove = 0 const MOVE_INTERVAL = 120 const tick = () => { if (!isActiveRef.current) return const now = performance.now() if (now - lastMove < MOVE_INTERVAL) { rafRef.current = requestAnimationFrame(tick) return } lastMove = now const container = containerRef.current if (!container) { rafRef.current = requestAnimationFrame(tick); return } const containerRect = container.getBoundingClientRect() if (containerRect.width === 0 || containerRect.height === 0) { rafRef.current = requestAnimationFrame(tick); return } const currentTargetId = targetIdRef.current let tx: number | null = null let ty: number | null = null if (currentTargetId) { const nodeElement = container.querySelector(`[data-id="${currentTargetId}"]`) as HTMLElement | null if (nodeElement) { const nodeRect = nodeElement.getBoundingClientRect() tx = nodeRect.left - containerRect.left + nodeRect.width / 2 ty = nodeRect.top - containerRect.top + nodeRect.height / 2 } } if (tx === null || ty === null) { if (posRef.current.visible) { posRef.current.visible = false if (elRef.current) elRef.current.style.opacity = '0' } rafRef.current = requestAnimationFrame(tick) return } const pos = posRef.current const speed = 0.15 const newX = pos.x + (tx - pos.x) * speed const newY = pos.y + (ty - pos.y) * speed if (!pos.visible) { pos.x = tx pos.y = ty pos.visible = true if (elRef.current) { elRef.current.style.opacity = '1' elRef.current.style.transform = `translate(${tx}px, ${ty}px)` } } else { pos.x = isNaN(newX) ? tx : newX pos.y = isNaN(newY) ? ty : newY if (elRef.current) { elRef.current.style.transform = `translate(${pos.x}px, ${pos.y}px)` } } rafRef.current = requestAnimationFrame(tick) } rafRef.current = requestAnimationFrame(tick) return () => { if (rafRef.current) cancelAnimationFrame(rafRef.current) } }, [isActive, containerRef]) return (
AI
) }