From f978153a83e2a68868f6ddcca775a85c7711c54e Mon Sep 17 00:00:00 2001 From: Hiren Kangad Date: Fri, 13 Mar 2026 11:33:57 +0530 Subject: [PATCH] fix: improve text sharpness on zoom with tighter buckets and reliable init Fix text staying blurry on zoom by ensuring the first setZoomBucket call always applies (PixiJS v8 defaults to auto-resolution which skipped the initial bucket=1 assignment), listening on both 'moved' and 'zoomed' viewport events, and tightening bucket thresholds from 6 to 9 levels. --- frontend/src/canvas/sprites/TextCore.ts | 6 +++++- frontend/src/canvas/textSharpness.ts | 13 ++++++++----- frontend/src/hooks/useCanvasSetup.ts | 3 +++ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/frontend/src/canvas/sprites/TextCore.ts b/frontend/src/canvas/sprites/TextCore.ts index 0af281c..5805724 100644 --- a/frontend/src/canvas/sprites/TextCore.ts +++ b/frontend/src/canvas/sprites/TextCore.ts @@ -35,6 +35,7 @@ export class TextCore { private _lastWordWrapWidth: number; private _lastLineHeightMultiplier: number; private _zoomBucket = 1; + private _zoomBucketApplied = false; get measuredWidth(): number { return this.pixiText.width; @@ -81,8 +82,11 @@ export class TextCore { * Only re-rasterizes when the bucket actually changes. */ setZoomBucket(bucket: number): void { - if (bucket === this._zoomBucket) return; + // Always apply on first call (resolution starts as null/auto in PixiJS v8). + // After that, skip if bucket hasn't changed. + if (this._zoomBucketApplied && bucket === this._zoomBucket) return; this._zoomBucket = bucket; + this._zoomBucketApplied = true; this.pixiText.resolution = bucket; } diff --git a/frontend/src/canvas/textSharpness.ts b/frontend/src/canvas/textSharpness.ts index 72a1970..82c064b 100644 --- a/frontend/src/canvas/textSharpness.ts +++ b/frontend/src/canvas/textSharpness.ts @@ -21,11 +21,14 @@ import { StickySprite } from './sprites/StickySprite'; * Fewer buckets = fewer re-rasterizations = better perf. */ export function getTextZoomBucket(scale: number): number { - if (scale < 0.4) return 0.5; - if (scale < 0.75) return 0.75; - if (scale < 1.25) return 1; - if (scale < 1.75) return 1.5; - if (scale < 2.5) return 2; + if (scale < 0.3) return 0.25; + if (scale < 0.5) return 0.5; + if (scale < 0.8) return 0.75; + if (scale < 1.1) return 1; + if (scale < 1.4) return 1.25; + if (scale < 1.8) return 1.5; + if (scale < 2.2) return 2; + if (scale < 3.0) return 2.5; return 3; } diff --git a/frontend/src/hooks/useCanvasSetup.ts b/frontend/src/hooks/useCanvasSetup.ts index 34d2bfc..14bcf65 100644 --- a/frontend/src/hooks/useCanvasSetup.ts +++ b/frontend/src/hooks/useCanvasSetup.ts @@ -185,12 +185,15 @@ export function useCanvasSetup(deps: CanvasSetupDeps) { undoRef.current = new UndoManager(scene); // Zoom-bucket text sharpness — re-rasterize visible text when zoom crosses bucket boundary + // Listen on both 'zoomed' and 'moved' — pixi-viewport may only fire 'moved' for some zoom types const sharpness = new TextSharpnessManager(viewport, scene); const onZoomBucketCheck = () => { sharpness.check(); }; viewport.on('zoomed', onZoomBucketCheck); + viewport.on('moved', onZoomBucketCheck); scene.onItemCreated = (item) => { sharpness.applyToItem(item); }; sharpnessCleanupRef.current = () => { viewport.off('zoomed', onZoomBucketCheck); + viewport.off('moved', onZoomBucketCheck); scene.onItemCreated = null; sharpness.destroy(); };