Browse Source

标注

feature/handleReconstruction
Luckyu_js 17 hours ago
parent
commit
27354e9848
  1. 2
      src/components/composer/NodeGenerationComposer.tsx
  2. 48
      src/components/media/MediaAnnotationOverlay.tsx

2
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;

48
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<MediaAnnotationOverlayHandle, MediaAnnotationOverlayProps>(function MediaAnnotationOverlay({
mediaElementRef,
imageSource,
naturalWidth,
naturalHeight,
annotations,
selectedShapeId,
currentTool,
@ -152,19 +138,14 @@ export const MediaAnnotationOverlay = forwardRef<MediaAnnotationOverlayHandle, M
const [currentShape, setCurrentShape] = useState<AnnotationShape | null>(null);
const [editingTextId, setEditingTextId] = useState<string | null>(null);
const [pendingTextPosition, setPendingTextPosition] = useState<{ x: number; y: number } | null>(null);
const [naturalDimensions, setNaturalDimensions] = useState<{ width: number; height: number } | null>(() =>
validDimensions(naturalWidth, naturalHeight)
);
// 加载用于回显的图片(走代理避免跨域污染),既作为 Stage 底图也用于导出。
// 加载底图(走代理避免跨域污染):仅用于导出时的干净合成。回显阶段画布下方
// 已有节点自身的 <img>(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<MediaAnnotationOverlayHandle, M
};
}, [imageSource]);
useEffect(() => {
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;

Loading…
Cancel
Save