From f254e9528a1bea33976243327c69edf5b3335195 Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Thu, 22 Jan 2026 08:23:02 +0000
Subject: [PATCH] Optimize FancyCursor by replacing elementFromPoint with event
delegation
---
src/components/layout/FancyCursor.test.tsx | 111 +++++++++++++++++++++
src/components/layout/FancyCursor.tsx | 14 ++-
2 files changed, 124 insertions(+), 1 deletion(-)
create mode 100644 src/components/layout/FancyCursor.test.tsx
diff --git a/src/components/layout/FancyCursor.test.tsx b/src/components/layout/FancyCursor.test.tsx
new file mode 100644
index 0000000..6b433d6
--- /dev/null
+++ b/src/components/layout/FancyCursor.test.tsx
@@ -0,0 +1,111 @@
+// @vitest-environment jsdom
+import { render, fireEvent, act } from '@testing-library/react';
+import { FancyCursor } from './FancyCursor';
+import { describe, it, expect, vi, beforeAll, afterAll, beforeEach } from 'vitest';
+import React from 'react';
+
+describe('FancyCursor', () => {
+ beforeAll(() => {
+ // Mock elementFromPoint for JSDOM
+ // @ts-ignore
+ document.elementFromPoint = vi.fn((x, y) => null);
+
+ // Mock requestAnimationFrame
+ vi.useFakeTimers();
+ });
+
+ afterAll(() => {
+ vi.useRealTimers();
+ // @ts-ignore
+ delete document.elementFromPoint;
+ });
+
+ beforeEach(() => {
+ vi.clearAllMocks();
+
+ // Ensure we are not seen as a touch device
+ Object.defineProperty(navigator, 'maxTouchPoints', { value: 0, writable: true });
+ // @ts-ignore
+ delete window.ontouchstart;
+ // console.log('Test Setup:', { ontouchstart: 'ontouchstart' in window, maxTouchPoints: navigator.maxTouchPoints });
+ });
+
+ it('renders and updates position on mousemove', () => {
+ const { container } = render();
+ // Using firstElementChild because querySelector seems flaky in this test environment
+ const cursor = container.firstElementChild as HTMLElement;
+ expect(cursor).toBeTruthy();
+ expect(cursor.id).toBe('fancy-cursor');
+
+ fireEvent.mouseMove(window, { clientX: 100, clientY: 100 });
+
+ act(() => {
+ vi.runAllTimers();
+ });
+
+ // Check transform
+ expect(cursor.style.transform).toContain('translate3d(100px, 100px, 0)');
+ });
+
+ it('toggles link-hover class when hovering a link', () => {
+ // We need to attach the link to the document body because FancyCursor uses global event listeners
+ // and checks the document structure via events or elementFromPoint.
+ const link = document.createElement('a');
+ link.href = '#';
+ link.id = 'test-link';
+ link.textContent = 'Link';
+ document.body.appendChild(link);
+
+ const { container } = render();
+ const cursor = container.firstElementChild as HTMLElement;
+ expect(cursor).toBeTruthy();
+
+ // Simulate mouse over the link.
+ // Important: in our optimized code, we rely on 'mouseover' event to set 'hoveredElement'.
+ fireEvent.mouseOver(link);
+
+ // Then mouseMove triggers the update loop
+ fireEvent.mouseMove(window, { clientX: 50, clientY: 50 });
+
+ act(() => {
+ vi.runAllTimers();
+ });
+
+ expect(cursor.classList.contains('link-hover')).toBe(true);
+
+ // Move away to body
+ fireEvent.mouseOver(document.body);
+ fireEvent.mouseMove(window, { clientX: 200, clientY: 200 });
+
+ act(() => {
+ vi.runAllTimers();
+ });
+
+ expect(cursor.classList.contains('link-hover')).toBe(false);
+
+ document.body.removeChild(link);
+ });
+
+ it('uses fallback elementFromPoint if hoveredElement is null', () => {
+ // Reset elementFromPoint mock
+ const mockElementFromPoint = vi.fn();
+ // @ts-ignore
+ document.elementFromPoint = mockElementFromPoint;
+
+ // We need to remount to reset internal state if any.
+ // However, 'hoveredElement' variable was defined INSIDE the component in my edit.
+ // Let's double check that.
+
+ render();
+
+ // Trigger mousemove WITHOUT prior mouseover.
+ // This should trigger the fallback.
+ fireEvent.mouseMove(window, { clientX: 10, clientY: 10 });
+
+ act(() => {
+ vi.runAllTimers();
+ });
+
+ expect(mockElementFromPoint).toHaveBeenCalledWith(10, 10);
+ });
+});
diff --git a/src/components/layout/FancyCursor.tsx b/src/components/layout/FancyCursor.tsx
index c7e98c4..27d6c5c 100644
--- a/src/components/layout/FancyCursor.tsx
+++ b/src/components/layout/FancyCursor.tsx
@@ -19,6 +19,11 @@ export const FancyCursor = memo(() => {
if (!cursor) return;
let animationFrameId: number;
+ let hoveredElement: Element | null = null;
+
+ const handleMouseOver = (e: MouseEvent) => {
+ hoveredElement = e.target as Element;
+ };
// The mouse move handler is throttled with requestAnimationFrame to ensure
// the animation is smooth and doesn't cause performance issues.
@@ -31,7 +36,12 @@ export const FancyCursor = memo(() => {
// 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%)`;
- const el = document.elementFromPoint(e.clientX, e.clientY);
+ // Use the cached hovered element if available, otherwise fallback to elementFromPoint
+ // This fallback is mostly for the initial state before any mouseover events
+ if (!hoveredElement) {
+ hoveredElement = document.elementFromPoint(e.clientX, e.clientY);
+ }
+ const el = hoveredElement;
const classList = cursor.classList;
// Coercing to boolean with `!!` is a micro-optimization.
@@ -49,11 +59,13 @@ export const FancyCursor = memo(() => {
// 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.