{
- const rect = overlayRef.current?.getBoundingClientRect();
- if (!rect) return { x: 0, y: 0 };
- return { x: event.clientX - rect.left, y: event.clientY - rect.top };
+ const el = overlayRef.current;
+ const rect = el?.getBoundingClientRect();
+ if (!rect || !el) return { x: 0, y: 0 };
+ // rect 是缩放后的屏幕尺寸,clientWidth 是未缩放的布局尺寸,二者比值即当前
+ // 有效缩放。除以它把指针位移还原到 overlay 自身坐标系(缩放无关)。
+ const scaleX = el.clientWidth > 0 ? rect.width / el.clientWidth : 1;
+ const scaleY = el.clientHeight > 0 ? rect.height / el.clientHeight : 1;
+ return { x: (event.clientX - rect.left) / scaleX, y: (event.clientY - rect.top) / scaleY };
}, []);
const handlePointerDown = useCallback((event: React.PointerEvent, mode: DragMode) => {
diff --git a/src/components/media/MediaOutpaintingOverlay.tsx b/src/components/media/MediaOutpaintingOverlay.tsx
index 55748789..a3e273db 100644
--- a/src/components/media/MediaOutpaintingOverlay.tsx
+++ b/src/components/media/MediaOutpaintingOverlay.tsx
@@ -170,9 +170,14 @@ export function MediaOutpaintingOverlay({
}, [measureBounds]);
const pointerToOverlay = useCallback((event: PointerEvent | React.PointerEvent) => {
- const rect = overlayRef.current?.getBoundingClientRect();
- if (!rect) return { x: 0, y: 0 };
- return { x: event.clientX - rect.left, y: event.clientY - rect.top };
+ const el = overlayRef.current;
+ const rect = el?.getBoundingClientRect();
+ if (!rect || !el) return { x: 0, y: 0 };
+ // rect 是缩放后的屏幕尺寸,clientWidth 是未缩放的布局尺寸,二者比值即当前
+ // 有效缩放。除以它把指针位移还原到 overlay 自身坐标系(缩放无关)。
+ const scaleX = el.clientWidth > 0 ? rect.width / el.clientWidth : 1;
+ const scaleY = el.clientHeight > 0 ? rect.height / el.clientHeight : 1;
+ return { x: (event.clientX - rect.left) / scaleX, y: (event.clientY - rect.top) / scaleY };
}, []);
const handlePointerDown = useCallback((event: React.PointerEvent, mode: DragMode) => {
diff --git a/src/utils/nodeDimensions.ts b/src/utils/nodeDimensions.ts
index 6f96cdb9..69b91b6c 100644
--- a/src/utils/nodeDimensions.ts
+++ b/src/utils/nodeDimensions.ts
@@ -380,3 +380,17 @@ export function calculateNodeSizeForFullBleed(
height: Math.round(height),
};
}
+
+/**
+ * 节点尺寸真相源:node.style.{width,height}(由 workflowStore / canvasAutoLayout 写入)。
+ * 仅在两者都是数字时返回,否则返回 null(调用方回退到 defaultNodeDimensions)。
+ * 抽到 utils 供 WorkflowCanvas 与画布级工具宿主共用,避免二者互相 import 形成循环依赖。
+ */
+export function getWorkflowNodeStyleDimensions(node: {
+ style?: { width?: unknown; height?: unknown } | null;
+}): { width: number; height: number } | null {
+ const width = typeof node.style?.width === "number" ? node.style.width : null;
+ const height = typeof node.style?.height === "number" ? node.style.height : null;
+ if (width === null || height === null) return null;
+ return { width, height };
+}