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.
This commit is contained in:
Hiren Kangad
2026-03-13 11:33:57 +05:30
parent 470693266e
commit f978153a83
3 changed files with 16 additions and 6 deletions
+5 -1
View File
@@ -35,6 +35,7 @@ export class TextCore {
private _lastWordWrapWidth: number; private _lastWordWrapWidth: number;
private _lastLineHeightMultiplier: number; private _lastLineHeightMultiplier: number;
private _zoomBucket = 1; private _zoomBucket = 1;
private _zoomBucketApplied = false;
get measuredWidth(): number { get measuredWidth(): number {
return this.pixiText.width; return this.pixiText.width;
@@ -81,8 +82,11 @@ export class TextCore {
* Only re-rasterizes when the bucket actually changes. * Only re-rasterizes when the bucket actually changes.
*/ */
setZoomBucket(bucket: number): void { 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._zoomBucket = bucket;
this._zoomBucketApplied = true;
this.pixiText.resolution = bucket; this.pixiText.resolution = bucket;
} }
+8 -5
View File
@@ -21,11 +21,14 @@ import { StickySprite } from './sprites/StickySprite';
* Fewer buckets = fewer re-rasterizations = better perf. * Fewer buckets = fewer re-rasterizations = better perf.
*/ */
export function getTextZoomBucket(scale: number): number { export function getTextZoomBucket(scale: number): number {
if (scale < 0.4) return 0.5; if (scale < 0.3) return 0.25;
if (scale < 0.75) return 0.75; if (scale < 0.5) return 0.5;
if (scale < 1.25) return 1; if (scale < 0.8) return 0.75;
if (scale < 1.75) return 1.5; if (scale < 1.1) return 1;
if (scale < 2.5) return 2; 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; return 3;
} }
+3
View File
@@ -185,12 +185,15 @@ export function useCanvasSetup(deps: CanvasSetupDeps) {
undoRef.current = new UndoManager(scene); undoRef.current = new UndoManager(scene);
// Zoom-bucket text sharpness — re-rasterize visible text when zoom crosses bucket boundary // 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 sharpness = new TextSharpnessManager(viewport, scene);
const onZoomBucketCheck = () => { sharpness.check(); }; const onZoomBucketCheck = () => { sharpness.check(); };
viewport.on('zoomed', onZoomBucketCheck); viewport.on('zoomed', onZoomBucketCheck);
viewport.on('moved', onZoomBucketCheck);
scene.onItemCreated = (item) => { sharpness.applyToItem(item); }; scene.onItemCreated = (item) => { sharpness.applyToItem(item); };
sharpnessCleanupRef.current = () => { sharpnessCleanupRef.current = () => {
viewport.off('zoomed', onZoomBucketCheck); viewport.off('zoomed', onZoomBucketCheck);
viewport.off('moved', onZoomBucketCheck);
scene.onItemCreated = null; scene.onItemCreated = null;
sharpness.destroy(); sharpness.destroy();
}; };