Optimize FancyCursor: Move DOM checks to mouseover #8

Merged
google-labs-jules[bot] merged 1 commits from optimize-fancy-cursor-13866096887027914754 into main 2026-01-23 09:55:22 +00:00
3 changed files with 21 additions and 29 deletions
Showing only changes of commit d5b079d452 - Show all commits

View File

@@ -86,26 +86,22 @@ describe('FancyCursor', () => {
document.body.removeChild(link); document.body.removeChild(link);
}); });
it('uses fallback elementFromPoint if hoveredElement is null', () => { it('does not call elementFromPoint on mousemove', () => {
// Reset elementFromPoint mock // Reset elementFromPoint mock
const mockElementFromPoint = vi.fn(); const mockElementFromPoint = vi.fn();
// @ts-ignore // @ts-ignore
document.elementFromPoint = mockElementFromPoint; 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(<FancyCursor />); render(<FancyCursor />);
// Trigger mousemove WITHOUT prior mouseover. // Trigger mousemove
// This should trigger the fallback.
fireEvent.mouseMove(window, { clientX: 10, clientY: 10 }); fireEvent.mouseMove(window, { clientX: 10, clientY: 10 });
act(() => { act(() => {
vi.runAllTimers(); vi.runAllTimers();
}); });
expect(mockElementFromPoint).toHaveBeenCalledWith(10, 10); // Should NOT be called in the optimized version
expect(mockElementFromPoint).not.toHaveBeenCalled();
}); });
}); });

View File

@@ -19,10 +19,24 @@ export const FancyCursor = memo(() => {
if (!cursor) return; if (!cursor) return;
let animationFrameId: number; let animationFrameId: number;
let hoveredElement: Element | null = null;
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) => { const handleMouseOver = (e: MouseEvent) => {
hoveredElement = e.target as Element; updateCursorState(e.target as Element);
}; };
// The mouse move handler is throttled with requestAnimationFrame to ensure // The mouse move handler is throttled with requestAnimationFrame to ensure
@@ -35,25 +49,6 @@ export const FancyCursor = memo(() => {
// This shifts the cursor element by half its own width and height, // This shifts the cursor element by half its own width and height,
// which effectively centers it on the pointer without affecting the visuals. // 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%)`; cursor.style.transform = `translate3d(${e.clientX}px, ${e.clientY}px, 0) translate(-50%, -50%)`;
// 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.
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);
}); });
}; };

View File

@@ -1,3 +1,4 @@
// @vitest-environment jsdom
import { render, fireEvent, act, cleanup } from "@testing-library/react"; import { render, fireEvent, act, cleanup } from "@testing-library/react";
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { Hero } from "./Hero"; import { Hero } from "./Hero";