diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..dfba806 --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,4 @@ +## 2024-08-02 - Canvas Optimization + +**Learning:** Re-assigning canvas dimensions (`canvas.width = canvas.width`) forces the browser to re-allocate the underlying graphics buffer and clear the canvas, which is an expensive operation that can cause layout thrashing. This is a common performance bottleneck in canvas-heavy React components like `PreviewCanvas.tsx` when re-rendering based on user interaction (like mouse moves or drawing annotations). +**Action:** When updating canvas dimensions within a `useCallback` or `useEffect` render pipeline, conditionally set them only if they have actually changed (`if (canvas.width !== targetW) canvas.width = targetW`). Since avoiding dimension re-assignment stops the implicit clear, explicitly call `clearRect` when the dimensions remain the same to prevent visual ghosting/artifacts. diff --git a/src/components/PreviewCanvas.tsx b/src/components/PreviewCanvas.tsx index 1aff08e..b454ba8 100644 --- a/src/components/PreviewCanvas.tsx +++ b/src/components/PreviewCanvas.tsx @@ -324,12 +324,14 @@ export const PreviewCanvas: React.FC = ({ } } - if (direction === 'vertical') { - baseCanvas.width = totalW; - baseCanvas.height = totalH; - } else { - baseCanvas.width = totalH; - baseCanvas.height = baseW; + const targetW = direction === 'vertical' ? totalW : totalH; + const targetH = direction === 'vertical' ? totalH : baseW; + + if (baseCanvas.width !== targetW) { + baseCanvas.width = targetW; + } + if (baseCanvas.height !== targetH) { + baseCanvas.height = targetH; } // Clean canvas @@ -512,10 +514,20 @@ export const PreviewCanvas: React.FC = ({ if (shouldShowMockup) { drawMockup(canvasRef.current, baseStitchedCanvas, mockup); } else { - canvasRef.current.width = baseStitchedCanvas.width; - canvasRef.current.height = baseStitchedCanvas.height; + let resized = false; + if (canvasRef.current.width !== baseStitchedCanvas.width) { + canvasRef.current.width = baseStitchedCanvas.width; + resized = true; + } + if (canvasRef.current.height !== baseStitchedCanvas.height) { + canvasRef.current.height = baseStitchedCanvas.height; + resized = true; + } const ctx = canvasRef.current.getContext('2d'); if (ctx) { + if (!resized) { + ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height); + } ctx.drawImage(baseStitchedCanvas, 0, 0); } }