From 80e98a939162dcc9769c154c6d88b7bd9235e26c Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 14 Jan 2026 04:37:48 +0000 Subject: [PATCH 1/2] perf: throttle mousemove events with requestAnimationFrame Prevents excessive layout thrashing and calculations by syncing updates to the browser's refresh rate. - Wraps update logic in `EventHandler.ts` with `requestAnimationFrame`. - Updates `lastKnownMousePosition` synchronously to ensure accuracy. - Reduces CPU usage during rapid mouse movement. --- .jules/bolt.md | 3 +++ src/magnify-glass/EventHandler.ts | 45 ++++++++++++++++++++----------- 2 files changed, 33 insertions(+), 15 deletions(-) create mode 100644 .jules/bolt.md diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..d37a631 --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2024-05-23 - Throttling High-Frequency Events +**Learning:** High-frequency events like `mousemove` can cause performance issues if they trigger expensive DOM updates or calculations on every firing. +**Action:** Use `requestAnimationFrame` to throttle these updates to the screen refresh rate. Ensure that the event data (like coordinates) is captured synchronously in the event handler so that the rAF callback has access to the latest state. diff --git a/src/magnify-glass/EventHandler.ts b/src/magnify-glass/EventHandler.ts index 8485b70..5ded1f1 100644 --- a/src/magnify-glass/EventHandler.ts +++ b/src/magnify-glass/EventHandler.ts @@ -16,6 +16,8 @@ import type { MagnifyGlass } from './MagnifyGlass'; export class EventHandler { magnifyGlass: MagnifyGlass; + private rafId: number | null = null; + constructor(magnifyGlass: MagnifyGlass) { this.magnifyGlass = magnifyGlass; @@ -222,26 +224,39 @@ export class EventHandler { if (!this.magnifyGlass.state.active || !this.magnifyGlass.litegraphCanvas) return; - const rect = this.magnifyGlass.litegraphCanvas.getBoundingClientRect(); - const cssMouseXOnCanvas = e.clientX - rect.left; - const cssMouseYOnCanvas = e.clientY - rect.top; + // Throttle updates using requestAnimationFrame to prevent excessive calculations and layout thrashing + if (this.rafId === null) { + this.rafId = requestAnimationFrame(() => { + this.rafId = null; - // Check if the cursor is over the canvas element - if (cssMouseXOnCanvas >= 0 && cssMouseXOnCanvas <= rect.width && - cssMouseYOnCanvas >= 0 && cssMouseYOnCanvas <= rect.height) { + // Re-check active state in case it changed while waiting for frame + if (!this.magnifyGlass.state.active || !this.magnifyGlass.litegraphCanvas) return; - const canvasElement = this.magnifyGlass.litegraphCanvas; - const scaleX = rect.width > 0 ? canvasElement.width / rect.width : 1; - const scaleY = rect.height > 0 ? canvasElement.height / rect.height : 1; + const clientX = this.magnifyGlass.lastKnownMousePosition.x; + const clientY = this.magnifyGlass.lastKnownMousePosition.y; - const pixelX = cssMouseXOnCanvas * scaleX; - const pixelY = cssMouseYOnCanvas * scaleY; + const rect = this.magnifyGlass.litegraphCanvas.getBoundingClientRect(); + const cssMouseXOnCanvas = clientX - rect.left; + const cssMouseYOnCanvas = clientY - rect.top; - this.magnifyGlass.state.x = pixelX; - this.magnifyGlass.state.y = pixelY; + // Check if the cursor is over the canvas element + if (cssMouseXOnCanvas >= 0 && cssMouseXOnCanvas <= rect.width && + cssMouseYOnCanvas >= 0 && cssMouseYOnCanvas <= rect.height) { - this.magnifyGlass.ui.positionGlass(e.clientX, e.clientY); - this.magnifyGlass.updateMagnifiedView(); + const canvasElement = this.magnifyGlass.litegraphCanvas; + const scaleX = rect.width > 0 ? canvasElement.width / rect.width : 1; + const scaleY = rect.height > 0 ? canvasElement.height / rect.height : 1; + + const pixelX = cssMouseXOnCanvas * scaleX; + const pixelY = cssMouseYOnCanvas * scaleY; + + this.magnifyGlass.state.x = pixelX; + this.magnifyGlass.state.y = pixelY; + + this.magnifyGlass.ui.positionGlass(clientX, clientY); + this.magnifyGlass.updateMagnifiedView(); + } + }); } } From 7bef79daa1088658ec32612c7553118370d0b850 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 14 Jan 2026 04:49:58 +0000 Subject: [PATCH 2/2] perf: throttle mousemove events with requestAnimationFrame Prevents excessive layout thrashing and calculations by syncing updates to the browser's refresh rate. - Wraps update logic in `EventHandler.ts` with `requestAnimationFrame`. - Updates `lastKnownMousePosition` synchronously to ensure accuracy. - Reduces CPU usage during rapid mouse movement. - Properly cancels animation frame on cleanup. --- src/magnify-glass/EventHandler.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/magnify-glass/EventHandler.ts b/src/magnify-glass/EventHandler.ts index 5ded1f1..f25a4c8 100644 --- a/src/magnify-glass/EventHandler.ts +++ b/src/magnify-glass/EventHandler.ts @@ -45,6 +45,11 @@ export class EventHandler { document.removeEventListener("keyup", this.handleKeyUp); document.removeEventListener("mousemove", this.handleMouseMove); window.removeEventListener("resize", this.handleResize); + + if (this.rafId !== null) { + cancelAnimationFrame(this.rafId); + this.rafId = null; + } } /**