import { memo, useEffect, useRef, useState } from 'react'; import './FancyCursor.css'; // This is the new, more performant implementation for the custom cursor. // It uses requestAnimationFrame for smooth movement and direct DOM manipulation for speed. export const FancyCursor = memo(() => { const cursorRef = useRef(null); const [isTouch, setIsTouch] = useState(false); useEffect(() => { // Check for touch devices once at the beginning. If it's a touch device, // the custom cursor will not be rendered. if (typeof window !== 'undefined' && ('ontouchstart' in window || navigator.maxTouchPoints > 0)) { setIsTouch(true); return; } const cursor = cursorRef.current; if (!cursor) return; let animationFrameId: number; const updateCursorState = (el: Element | null) => { const classList = cursor.classList; // Coercing to boolean with `!!` is a micro-optimization. const isResize = !!el?.closest('#custom-resize-handle'); const isText = !!el?.closest('input[type="text"], input[type="email"], textarea, [contenteditable="true"]'); const isLink = !!el?.closest('a, button, [role="button"], input[type="submit"], input[type="button"]'); // These classes are toggled based on the hovered element. // The actual visual styles are defined in your CSS files. classList.toggle('resize-hover', isResize); classList.toggle('text-hover', isText && !isResize); classList.toggle('link-hover', isLink && !isText && !isResize); }; const handleMouseOver = (e: MouseEvent) => { updateCursorState(e.target as Element); }; // The mouse move handler is throttled with requestAnimationFrame to ensure // the animation is smooth and doesn't cause performance issues. const handleMouseMove = (e: MouseEvent) => { cancelAnimationFrame(animationFrameId); animationFrameId = requestAnimationFrame(() => { // The offset issue was caused by positioning the cursor's top-left corner // at the mouse coordinates. To fix this, `translate(-50%, -50%)` is added. // This shifts the cursor element by half its own width and height, // which effectively centers it on the pointer without affecting the visuals. cursor.style.transform = `translate3d(${e.clientX}px, ${e.clientY}px, 0) translate(-50%, -50%)`; }); }; // Using a passive event listener can improve scrolling performance. window.addEventListener('mousemove', handleMouseMove, { passive: true }); window.addEventListener('mouseover', handleMouseOver, { passive: true }); // The cleanup function removes the event listener when the component unmounts // to prevent memory leaks. return () => { window.removeEventListener('mousemove', handleMouseMove); window.removeEventListener('mouseover', handleMouseOver); cancelAnimationFrame(animationFrameId); }; }, []); // The empty dependency array ensures this effect runs only once. if (isTouch) { return null; } return ( // `will-change` is a hint to the browser to optimize for transform changes.
{/* SVG updated to use currentColor to inherit from CSS */}
); });