⚡ Bolt: Optimized GradientBlinds resize performance #50
@@ -280,18 +280,36 @@ void main() {
|
|||||||
const mesh = new Mesh(gl, { geometry, program });
|
const mesh = new Mesh(gl, { geometry, program });
|
||||||
meshRef.current = mesh;
|
meshRef.current = mesh;
|
||||||
|
|
||||||
const resize = () => {
|
const resize = (entries?: ResizeObserverEntry[]) => {
|
||||||
|
let width, height;
|
||||||
|
|
||||||
|
if (entries && entries.length > 0) {
|
||||||
|
const entry = entries[0];
|
||||||
|
width = entry.contentRect.width;
|
||||||
|
|
|||||||
|
height = entry.contentRect.height;
|
||||||
|
// Optimization: Defer calling getBoundingClientRect (which forces layout)
|
||||||
|
// to the next frame, since ResizeObserver runs after layout but before paint.
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
if (rendererRef.current) {
|
||||||
|
rectRef.current = container.getBoundingClientRect();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
const rect = container.getBoundingClientRect();
|
const rect = container.getBoundingClientRect();
|
||||||
|
The requestAnimationFrame callback could execute after the component has unmounted. While there is a check for rendererRef.current, this doesn't prevent the container.getBoundingClientRect() call if the component unmounts between the check and the call. This could lead to accessing a detached DOM element. Add a cleanup mechanism to cancel pending requestAnimationFrame callbacks, or store the RAF ID and cancel it in the useEffect cleanup function. Alternatively, add an additional check that the container is still in the document before calling getBoundingClientRect(). The requestAnimationFrame callback could execute after the component has unmounted. While there is a check for rendererRef.current, this doesn't prevent the container.getBoundingClientRect() call if the component unmounts between the check and the call. This could lead to accessing a detached DOM element.
Add a cleanup mechanism to cancel pending requestAnimationFrame callbacks, or store the RAF ID and cancel it in the useEffect cleanup function. Alternatively, add an additional check that the container is still in the document before calling getBoundingClientRect().
```suggestion
// Ensure renderer is still active and container is still in the document
if (
!rendererRef.current ||
typeof document === 'undefined' ||
!container ||
!(container as HTMLElement).isConnected ||
!document.body.contains(container)
) {
return;
}
rectRef.current = container.getBoundingClientRect();
```
|
|||||||
rectRef.current = rect;
|
rectRef.current = rect;
|
||||||
|
The deferred update of rectRef.current can cause mouse position calculations to use stale position data. When a resize occurs, rectRef.current won't be updated until the next animation frame, but mouse events (via onPointerMove on lines 333-358 and the loop function on lines 360-409) may use the outdated rect during this gap. This can lead to incorrect mouse coordinate calculations, particularly noticeable during rapid resize operations. Consider updating rectRef.current synchronously using contentRect properties (left, top can be derived from container.getBoundingClientRect() on initial call), or accept that mouse position calculations may be slightly off during resize until the next frame. The deferred update of rectRef.current can cause mouse position calculations to use stale position data. When a resize occurs, rectRef.current won't be updated until the next animation frame, but mouse events (via onPointerMove on lines 333-358 and the loop function on lines 360-409) may use the outdated rect during this gap. This can lead to incorrect mouse coordinate calculations, particularly noticeable during rapid resize operations.
Consider updating rectRef.current synchronously using contentRect properties (left, top can be derived from container.getBoundingClientRect() on initial call), or accept that mouse position calculations may be slightly off during resize until the next frame.
```suggestion
// Use a synchronous rect update so mouse position calculations
// always see up-to-date geometry during resize.
const rect = container.getBoundingClientRect();
rectRef.current = rect;
width = rect.width;
height = rect.height;
```
|
|||||||
|
width = rect.width;
|
||||||
|
height = rect.height;
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
isMobileRef.current = window.innerWidth <= 768;
|
isMobileRef.current = window.innerWidth <= 768;
|
||||||
scrollPosRef.current = { x: window.scrollX, y: window.scrollY };
|
scrollPosRef.current = { x: window.scrollX, y: window.scrollY };
|
||||||
}
|
}
|
||||||
renderer.setSize(rect.width, rect.height);
|
renderer.setSize(width, height);
|
||||||
uniforms.iResolution.value = [gl.drawingBufferWidth, gl.drawingBufferHeight, 1];
|
uniforms.iResolution.value = [gl.drawingBufferWidth, gl.drawingBufferHeight, 1];
|
||||||
|
|
||||||
if (blindMinWidth && blindMinWidth > 0) {
|
if (blindMinWidth && blindMinWidth > 0) {
|
||||||
const maxByMinWidth = Math.max(1, Math.floor(rect.width / blindMinWidth));
|
const maxByMinWidth = Math.max(1, Math.floor(width / blindMinWidth));
|
||||||
|
|
||||||
const effective = blindCount ? Math.min(blindCount, maxByMinWidth) : maxByMinWidth;
|
const effective = blindCount ? Math.min(blindCount, maxByMinWidth) : maxByMinWidth;
|
||||||
uniforms.uBlindCount.value = Math.max(1, effective);
|
uniforms.uBlindCount.value = Math.max(1, effective);
|
||||||
|
|||||||
With
strict: trueintsconfig.json(which enablesnoImplicitAny),let width, height;is an implicitanyand will failpnpm exec tsc --noEmit(or any strict type check). This is a build-breaking regression introduced by the change; please add an explicitnumbertype or initialize the variables (e.g.,let width = 0, height = 0) so the file type-checks again.Useful? React with 👍 / 👎.