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(); };