From 27354e98480c53bb3e80ee1d04d62391723f0597 Mon Sep 17 00:00:00 2001 From: Luckyu_js <11670186+luckyu-js@user.noreply.gitee.com> Date: Tue, 14 Jul 2026 11:31:26 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A0=87=E6=B3=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../composer/NodeGenerationComposer.tsx | 2 +- .../media/MediaAnnotationOverlay.tsx | 48 ++++--------------- 2 files changed, 11 insertions(+), 39 deletions(-) diff --git a/src/components/composer/NodeGenerationComposer.tsx b/src/components/composer/NodeGenerationComposer.tsx index dc21c9f9..80f2ba8d 100644 --- a/src/components/composer/NodeGenerationComposer.tsx +++ b/src/components/composer/NodeGenerationComposer.tsx @@ -22,7 +22,7 @@ function isToolSupported(tool: NodeToolType, node: WorkflowNode | null): boolean if (tool === "multiAngle") return Boolean(getNodeImageSource(node)); if (tool === "highDefinition") return Boolean(node && isHighDefinitionDerivedImageNode(node.data)); if (tool === "generationComposer") return true; - if (tool === "crop" || tool === "splitGrid" || tool === "outpainting" || tool === "inpainting") { + if (tool === "crop" || tool === "splitGrid" || tool === "outpainting" || tool === "inpainting" || tool === "annotate") { return Boolean(getNodeImageSource(node)); } return false; diff --git a/src/components/media/MediaAnnotationOverlay.tsx b/src/components/media/MediaAnnotationOverlay.tsx index 79cefc87..d94d0139 100644 --- a/src/components/media/MediaAnnotationOverlay.tsx +++ b/src/components/media/MediaAnnotationOverlay.tsx @@ -59,17 +59,6 @@ const DEFAULT_TEXT_BOX_WIDTH = 180; const DEFAULT_TEXT_BOX_HEIGHT = 32; const FIXED_TEXT_FONT_SIZE = 24; -function validDimensions(width: unknown, height: unknown): { width: number; height: number } | null { - return typeof width === "number" && typeof height === "number" && width > 0 && height > 0 - ? { width, height } - : null; -} - -function readImageNaturalDimensions(image: HTMLImageElement | null): { width: number; height: number } | null { - if (!image) return null; - return validDimensions(image.naturalWidth, image.naturalHeight); -} - /** 把某个 shape 从显示空间按比例映射到导出(natural)空间。 */ function scaleShape(shape: AnnotationShape, scaleX: number, scaleY: number): AnnotationShape { const base = { @@ -126,10 +115,7 @@ function buildKonvaShape(shape: AnnotationShape): Konva.Shape | null { } export const MediaAnnotationOverlay = forwardRef(function MediaAnnotationOverlay({ - mediaElementRef, imageSource, - naturalWidth, - naturalHeight, annotations, selectedShapeId, currentTool, @@ -152,19 +138,14 @@ export const MediaAnnotationOverlay = forwardRef(null); const [editingTextId, setEditingTextId] = useState(null); const [pendingTextPosition, setPendingTextPosition] = useState<{ x: number; y: number } | null>(null); - const [naturalDimensions, setNaturalDimensions] = useState<{ width: number; height: number } | null>(() => - validDimensions(naturalWidth, naturalHeight) - ); - // 加载用于回显的图片(走代理避免跨域污染),既作为 Stage 底图也用于导出。 + // 加载底图(走代理避免跨域污染):仅用于导出时的干净合成。回显阶段画布下方 + // 已有节点自身的 (object-cover)透出,故底图加载失败也不影响绘制。 useEffect(() => { let cancelled = false; loadCropImage(imageSource) .then((image) => { - if (cancelled) return; - setDisplayImage(image); - const dims = readImageNaturalDimensions(image); - if (dims) setNaturalDimensions(dims); + if (!cancelled) setDisplayImage(image); }) .catch(() => { if (!cancelled) setDisplayImage(null); @@ -174,27 +155,18 @@ export const MediaAnnotationOverlay = forwardRef { - const explicit = validDimensions(naturalWidth, naturalHeight); - if (explicit) setNaturalDimensions(explicit); - }, [naturalWidth, naturalHeight]); - + // 绘制面 = 媒体框本身。节点图片以 object-cover 铺满媒体框(框的尺寸由节点 + // style 决定),故 overlay 自身的 client 矩形即绘制区,无需依赖图片自然尺寸; + // 自然尺寸仅在导出(exportComposite)时用于坐标换算。这样只要媒体框有布局 + // 尺寸,canvas 就一定渲染,不会再卡在“自然尺寸未知”而空白。 const measureBounds = useCallback((): DisplayRect | null => { const overlay = overlayRef.current; - if (!overlay || !naturalDimensions) return null; + if (!overlay) return null; const width = overlay.clientWidth; const height = overlay.clientHeight; if (width <= 0 || height <= 0) return null; - const scale = Math.min(width / naturalDimensions.width, height / naturalDimensions.height); - const displayWidth = naturalDimensions.width * scale; - const displayHeight = naturalDimensions.height * scale; - return { - left: (width - displayWidth) / 2, - top: (height - displayHeight) / 2, - width: displayWidth, - height: displayHeight, - }; - }, [naturalDimensions]); + return { left: 0, top: 0, width, height }; + }, []); useEffect(() => { const overlay = overlayRef.current;