diff --git a/excalidraw-app/App.tsx b/excalidraw-app/App.tsx index d7a93bbea..5fd135226 100644 --- a/excalidraw-app/App.tsx +++ b/excalidraw-app/App.tsx @@ -26,6 +26,10 @@ import { StoreAction, reconcileElements, } from "../packages/excalidraw"; +import { + exportToBlob, + getNonDeletedElements, +} from "../packages/excalidraw/index"; import type { AppState, ExcalidrawImperativeAPI, @@ -127,6 +131,9 @@ import DebugCanvas, { } from "./components/DebugCanvas"; import { AIComponents } from "./components/AI"; import { ExcalidrawPlusIframeExport } from "./ExcalidrawPlusIframeExport"; +import { fileSave } from "../packages/excalidraw/data/filesystem"; +import type { ExportToCanvasConfig } from "../packages/excalidraw/scene/export"; +import { exportToCanvas } from "../packages/utils"; polyfill(); @@ -607,6 +614,24 @@ const ExcalidrawWrapper = () => { }; }, [excalidrawAPI]); + const canvasPreviewContainerRef = useRef(null); + + const [config, setConfig] = useState( + JSON.parse(localStorage.getItem("_exportConfig") || "null") || { + width: 300, + height: 100, + padding: 2, + scale: 1, + position: "none", + fit: "contain", + canvasBackgroundColor: "yellow", + }, + ); + + useEffect(() => { + localStorage.setItem("_exportConfig", JSON.stringify(config)); + }, [config]); + const onChange = ( elements: readonly OrderedExcalidrawElement[], appState: AppState, @@ -616,6 +641,93 @@ const ExcalidrawWrapper = () => { collabAPI.syncElements(elements); } + { + const frame = elements.find( + (el) => el.strokeStyle === "dashed" && !el.isDeleted, + ); + + const zoom = appState.zoom.value; + + exportToCanvas({ + data: { + elements: getNonDeletedElements(elements).filter( + (x) => x.id !== frame?.id, + ), + // .concat( + // restoreElements( + // [ + // // @ts-ignore + // { + // type: "rectangle", + // width: appState.width / zoom, + // height: appState.height / zoom, + // x: -appState.scrollX, + // y: -appState.scrollY, + // fillStyle: "solid", + // strokeColor: "transparent", + // backgroundColor: "rgba(0,0,0,0.05)", + // roundness: { type: ROUNDNESS.ADAPTIVE_RADIUS, value: 40 }, + // }, + // ], + // null, + // ), + // ), + appState, + files, + }, + config: { + // // light yellow + // // canvasBackgroundColor: "#fff9c4", + // // width, + // // maxWidthOrHeight: 120, + // // scale: 0.01, + // // scale: 2, + // // origin: "content", + // // fit: "cover", + // // scale: 2, + // // x: 0, + // // y: 0, + // padding: 20, + + // ...config, + + // width: config.width, + // height: config.height, + // maxWidthOrHeight: config.maxWidthOrHeight, + // widthOrHeight: config.widthOrHeight, + // padding: config.padding, + ...(frame + ? { + ...config, + width: frame.width, + height: frame.height, + x: frame.x, + y: frame.y, + } + : config), + // // height: 140, + // // x: -appState.scrollX, + // // y: -appState.scrollY, + // // height: 150, + // // height: appState.height, + // // scale, + // // zoom: { value: appState.zoom.value }, + // // getDimensions(width,height) { + // // setCanvasSize({ width, height }) + // // return {width: 300, height: 150} + // // } + }, + }).then((canvas) => { + if (canvasPreviewContainerRef.current) { + canvasPreviewContainerRef.current.replaceChildren(canvas); + document.querySelector( + ".dims", + )!.innerHTML = `${canvas.width}x${canvas.height}`; + // canvas.style.width = "100%"; + } + }); + } + // this check is redundant, but since this is a hot path, it's best // not to evaludate the nested expression every time if (!LocalData.isSavePaused()) { @@ -1121,6 +1233,233 @@ const ExcalidrawWrapper = () => { /> )} +
+
+
+ + + + +
+
+ + + + + + +
+
+
0x0
+
{ + exportToBlob({ + data: { + elements: excalidrawAPI!.getSceneElements(), + files: null, + }, + config, + }).then((blob) => { + fileSave(blob, { + name: "xx", + extension: "png", + description: "xxx", + }); + }); + }} + style={{ + borderRadius: 12, + border: "1px solid #777", + overflow: "hidden", + padding: 10, + backgroundColor: "pink", + }} + /> +
); };