From 312ffd7cb47233e34ae85899f4e7b575ddc42bdc Mon Sep 17 00:00:00 2001 From: Fini Date: Tue, 24 Feb 2026 00:50:23 +0800 Subject: [PATCH] fix(canvas): ensure icon selection box is always square with centered content MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For icon nodes (PathNode with iconId), expand Fabric's logical bounding box to the declared w×h so selection handles always form a perfect square, while the path content remains centered via Fabric's natural pathOffset. --- src/canvas/canvas-object-factory.ts | 9 ++++++++- src/canvas/canvas-object-sync.ts | 8 +++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/canvas/canvas-object-factory.ts b/src/canvas/canvas-object-factory.ts index 742d7e8bf..1c92a33e8 100644 --- a/src/canvas/canvas-object-factory.ts +++ b/src/canvas/canvas-object-factory.ts @@ -320,7 +320,14 @@ export function createFabricObject( if (pw > 0 && ph > 0 && obj.width && obj.height) { // Uniform scale — preserve aspect ratio so icons don't get squished const uniformScale = Math.min(pw / obj.width, ph / obj.height) - obj.set({ scaleX: uniformScale, scaleY: uniformScale }) + if (node.iconId) { + // For icon nodes: expand the Fabric bounding box to pw×ph so the selection + // handles always form a perfect square. The path content is naturally centered + // by Fabric's pathOffset (center of path's bbox). + obj.set({ width: pw / uniformScale, height: ph / uniformScale, scaleX: uniformScale, scaleY: uniformScale }) + } else { + obj.set({ scaleX: uniformScale, scaleY: uniformScale }) + } } break } diff --git a/src/canvas/canvas-object-sync.ts b/src/canvas/canvas-object-sync.ts index 565f16a74..8eccc33f1 100644 --- a/src/canvas/canvas-object-sync.ts +++ b/src/canvas/canvas-object-sync.ts @@ -187,7 +187,13 @@ export function syncFabricObject( if (node.type === 'path') { // Uniform scale — preserve aspect ratio so icons don't get squished const uniformScale = Math.min(w / nw, h / nh) - obj.set({ width: nw, height: nh, scaleX: uniformScale, scaleY: uniformScale }) + if (node.iconId) { + // Expand bounding box to w×h so selection handles form a perfect square. + // Path content is centered automatically by Fabric's pathOffset. + obj.set({ width: w / uniformScale, height: h / uniformScale, scaleX: uniformScale, scaleY: uniformScale }) + } else { + obj.set({ width: nw, height: nh, scaleX: uniformScale, scaleY: uniformScale }) + } } else { obj.set({ width: nw, height: nh, scaleX: w / nw, scaleY: h / nh }) }