From d817d421744660bd860447afa6ed6bdaa94a9acc Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 1 Feb 2026 02:08:43 +0000 Subject: [PATCH] perf(effects): reduce GC pressure in GradientBlinds animation loop Refactors `GradientBlinds` to use mutable references for pointer position tracking instead of creating new objects on every `pointermove` event and animation frame. This reduces Garbage Collection overhead during high-frequency updates. - Changes `pointerPosRef` to a stable object with an `active` flag. - Updates `mouseTargetRef` (array) in-place instead of reassigning. - Ensures distinct array references for dampening logic to prevent aliasing bugs. Co-authored-by: ragusa-it <196988693+ragusa-it@users.noreply.github.com> --- src/components/effects/GradientBlinds.tsx | 29 ++++++++++++++++------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/components/effects/GradientBlinds.tsx b/src/components/effects/GradientBlinds.tsx index 9f3b787..efa72d3 100644 --- a/src/components/effects/GradientBlinds.tsx +++ b/src/components/effects/GradientBlinds.tsx @@ -65,7 +65,8 @@ const GradientBlinds: React.FC = ({ const rendererRef = useRef(null); const mouseTargetRef = useRef<[number, number]>([0, 0]); // Optimization: store raw pointer position (viewport coords) to decouple event handling from calculation - const pointerPosRef = useRef<{ x: number; y: number } | null>(null); + // Changed to a stable object to avoid GC pressure in high-frequency event handlers + const pointerPosRef = useRef<{ x: number; y: number; active: boolean }>({ x: 0, y: 0, active: false }); const isMobileRef = useRef(false); const lastTimeRef = useRef(0); const firstResizeRef = useRef(true); @@ -304,7 +305,8 @@ void main() { const cx = gl.drawingBufferWidth / 2; const cy = gl.drawingBufferHeight / 2; uniforms.iMouse.value = [cx, cy]; - mouseTargetRef.current = [cx, cy]; + mouseTargetRef.current[0] = cx; + mouseTargetRef.current[1] = cy; } }; @@ -332,10 +334,13 @@ void main() { x = (e.clientX - rect.left) * scale; y = (rect.height - (e.clientY - rect.top)) * scale; } - mouseTargetRef.current = [x, y]; - pointerPosRef.current = null; // Ensure loop doesn't override + mouseTargetRef.current[0] = x; + mouseTargetRef.current[1] = y; + pointerPosRef.current.active = false; // Ensure loop doesn't override } else { - pointerPosRef.current = { x: e.clientX, y: e.clientY }; + pointerPosRef.current.x = e.clientX; + pointerPosRef.current.y = e.clientY; + pointerPosRef.current.active = true; } }; @@ -344,7 +349,7 @@ void main() { uniforms.iTime.value = t * 0.001; // Update target based on pointer position and scroll offset - if (pointerPosRef.current) { + if (pointerPosRef.current.active) { const scale = (renderer as unknown as { dpr?: number }).dpr || 1; let x, y; @@ -361,7 +366,8 @@ void main() { x = (pointerPosRef.current.x - rect.left) * scale; y = (rect.height - (pointerPosRef.current.y - rect.top)) * scale; } - mouseTargetRef.current = [x, y]; + mouseTargetRef.current[0] = x; + mouseTargetRef.current[1] = y; } if (mouseDampening > 0) { @@ -376,8 +382,13 @@ void main() { cur[0] += (target[0] - cur[0]) * factor; cur[1] += (target[1] - cur[1]) * factor; } else { - if (pointerPosRef.current || isMobileRef.current) { - uniforms.iMouse.value = mouseTargetRef.current; + if (pointerPosRef.current.active || isMobileRef.current) { + // In no-dampening mode, update values directly. + // We copy values instead of assigning the reference to avoid aliasing issues + // (where cur and target become the same array) if dampening is enabled later + // without re-creating uniforms (though currently effect deps handle that). + uniforms.iMouse.value[0] = mouseTargetRef.current[0]; + uniforms.iMouse.value[1] = mouseTargetRef.current[1]; } lastTimeRef.current = t; } -- 2.49.1