- use-canvas-media-tools.ts: createPortraitTextureNode 改为 generatePortraitTextureNode, 节点创建后立即触发后端生成任务(与情绪/多视角/局部编辑保持一致): 使用 nodeReferenceImage 获取源图引用,buildPortraitTexturePrompt 构建提示词, runBackendCanvasGenerationTask 执行生成,完成后更新缩略图 - project.tsx: 同步重命名 createPortraitTextureNode → generatePortraitTextureNode - canvas-drawing-excalidraw-editor.tsx: getDimensions 参数补充 number 类型注解 - canvas-drawing-editor-modal.tsx: Pick 类型补充 engine 字段 Co-authored-by: sikongyue <3155157671@qq.com>
65 lines
3.2 KiB
TypeScript
65 lines
3.2 KiB
TypeScript
import { forwardRef, useImperativeHandle, useRef } from "react";
|
|
import { Excalidraw, exportToBlob, getNonDeletedElements, serializeAsJSON } from "@excalidraw/excalidraw";
|
|
import type { ExcalidrawImperativeAPI, ExcalidrawInitialDataState } from "@excalidraw/excalidraw/types";
|
|
import "@excalidraw/excalidraw/index.css";
|
|
|
|
import type { CanvasDrawingEditorHandle, CanvasDrawingEditorProps } from "@/components/canvas/canvas-drawing-editor-types";
|
|
|
|
const DRAWING_RENDER_MAX_DIMENSION = 2048;
|
|
const DRAWING_RENDER_PADDING = 24;
|
|
|
|
export const CanvasDrawingExcalidrawEditor = forwardRef<CanvasDrawingEditorHandle, CanvasDrawingEditorProps>(function CanvasDrawingExcalidrawEditor({ snapshot, colorScheme, onReady }, ref) {
|
|
const apiRef = useRef<ExcalidrawImperativeAPI | null>(null);
|
|
|
|
useImperativeHandle(ref, () => ({
|
|
createSave: async () => {
|
|
const api = apiRef.current;
|
|
if (!api) throw new Error("Excalidraw 编辑器尚未准备完成");
|
|
const elements = api.getSceneElementsIncludingDeleted();
|
|
const appState = api.getAppState();
|
|
const files = api.getFiles();
|
|
const serialized = serializeAsJSON(elements, appState, files, "local");
|
|
const savedSnapshot = JSON.parse(serialized) as unknown;
|
|
const visibleElements = getNonDeletedElements(elements);
|
|
if (!visibleElements.length) return { snapshot: savedSnapshot, preview: null, render: null };
|
|
|
|
let outputWidth = 1;
|
|
let outputHeight = 1;
|
|
const blob = await exportToBlob({
|
|
elements: visibleElements,
|
|
appState: { ...appState, exportBackground: true, exportWithDarkMode: false, viewBackgroundColor: "#ffffff" },
|
|
files,
|
|
mimeType: "image/png",
|
|
exportPadding: DRAWING_RENDER_PADDING,
|
|
getDimensions: (width: number, height: number) => {
|
|
const scale = Math.min(4, DRAWING_RENDER_MAX_DIMENSION / Math.max(1, width, height));
|
|
outputWidth = Math.max(1, Math.round(width * scale));
|
|
outputHeight = Math.max(1, Math.round(height * scale));
|
|
return { width: outputWidth, height: outputHeight, scale };
|
|
},
|
|
});
|
|
const render = { blob, pageId: "excalidraw-page", width: outputWidth, height: outputHeight, mimeType: blob.type || "image/png", background: "white" as const };
|
|
return { snapshot: savedSnapshot, preview: blob, render };
|
|
},
|
|
}), []);
|
|
|
|
const initialData = snapshot && typeof snapshot === "object"
|
|
? { ...(snapshot as ExcalidrawInitialDataState), scrollToContent: true }
|
|
: { elements: [], appState: { viewBackgroundColor: "#ffffff" }, files: {}, scrollToContent: true };
|
|
|
|
return (
|
|
<Excalidraw
|
|
initialData={initialData}
|
|
langCode="zh-CN"
|
|
theme={colorScheme}
|
|
autoFocus
|
|
excalidrawAPI={(api) => {
|
|
apiRef.current = api;
|
|
api.setActiveTool({ type: "freedraw" });
|
|
onReady();
|
|
}}
|
|
UIOptions={{ canvasActions: { loadScene: false, saveToActiveFile: false } }}
|
|
/>
|
|
);
|
|
});
|