From 088ed291a8ee851baed312731e4ded2a40ac5b3d Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Sat, 18 Apr 2026 17:10:56 +0700 Subject: [PATCH] perf(canvas): rAF-coalesced render, dirty imageData, viewport-bounded zoom/pan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mobile lag root cause: every pan/zoom tick ran putImageData(imageData, 0, 0), a ~67MB copy into the offscreen. On a phone, that dominates the frame budget. - CanvasRenderer: split render() into a deferred scheduler + doRender(). requestAnimationFrame coalesces redundant calls within one frame. An imageDataDirty flag gates putImageData — only re-upload when pixels actually changed (setPixelRgba / loadCanvas). Pan+zoom renders are now pure compositor work. - Zoom clamp: minZoom = min(viewportW/CW, viewportH/CH). Computed in App.svelte from a resize-tracked viewport, passed to CanvasRenderer (wheel + pinch use it) and CanvasControls (zoom-out button disables at bound). Replaces the hardcoded 0.25 that let phones see empty void. - Pan clamp: clampPan() runs before every paint. When canvas >= viewport, canvas always covers the screen. When canvas < viewport (never happens at minZoom, but safe), canvas stays inside. --- src/client/App.svelte | 35 ++++++++++++--- src/client/components/CanvasControls.svelte | 16 +++++-- src/client/components/CanvasRenderer.svelte | 50 +++++++++++++++++++-- 3 files changed, 86 insertions(+), 15 deletions(-) diff --git a/src/client/App.svelte b/src/client/App.svelte index 047c0e9..dcbf2c8 100644 --- a/src/client/App.svelte +++ b/src/client/App.svelte @@ -1,5 +1,5 @@