import { type Excalidraw } from '@excalidraw/excalidraw'; import { ExcalidrawElement } from '@excalidraw/excalidraw/types/element/types'; import { AppState, ExcalidrawImperativeAPI, } from '@excalidraw/excalidraw/types/types'; import { useCallback, useEffect, useState } from 'react'; import initialDrawing from './drawingState.json'; export type CanvasChangeEvent = { elements: readonly ExcalidrawElement[]; appState: AppState; imageData: string; }; export type DrawingCanvasProps = { onCanvasChange: (event: CanvasChangeEvent) => void; }; async function blobToBase64(blob: Blob): Promise { const reader = new FileReader(); reader.readAsDataURL(blob); return new Promise((resolve) => { reader.onloadend = () => { resolve(reader.result?.toString() || ''); }; }); } export function DrawingCanvas({ onCanvasChange }: DrawingCanvasProps) { const [ExcalidrawComponent, setExcalidrawComponent] = useState< typeof Excalidraw | null >(null); const [excalidrawAPI, setExcalidrawAPI] = useState(null); const [sceneData, setSceneData] = useState(null); useEffect(() => { import('@excalidraw/excalidraw').then((comp) => setExcalidrawComponent(comp.Excalidraw) ); const onResize = () => { if (excalidrawAPI) { excalidrawAPI.refresh(); } }; window.addEventListener('resize', onResize); return () => { window.removeEventListener('resize', onResize); }; }, []); const handleCanvasChanges = useCallback( async (elements: readonly ExcalidrawElement[], appState: AppState) => { if (!excalidrawAPI || !elements || !elements.length) { return; } const { exportToBlob, convertToExcalidrawElements, serializeAsJSON } = await import('@excalidraw/excalidraw'); const [boundingBoxElement] = convertToExcalidrawElements([ { type: 'rectangle', x: 0, y: 0, width: 512, height: 512, fillStyle: 'solid', backgroundColor: 'cyan', }, ]); const newSceneData = serializeAsJSON( elements, appState, excalidrawAPI.getFiles(), 'local' ); if (newSceneData !== sceneData) { setSceneData(newSceneData); const blob = await exportToBlob({ elements: [boundingBoxElement, ...elements], appState: { ...appState, frameRendering: { ...(appState.frameRendering || {}), clip: false, }, }, files: excalidrawAPI.getFiles(), mimeType: 'image/webp', quality: 0.5, exportPadding: 0, getDimensions: () => { return { width: 512, height: 512 }; }, }); const imageData = await blobToBase64(blob); onCanvasChange({ elements, appState, imageData }); } }, [excalidrawAPI, onCanvasChange, sceneData] ); return (
{ExcalidrawComponent && ( setExcalidrawAPI(api)} initialData={{ elements: initialDrawing as ExcalidrawElement[] }} onChange={handleCanvasChanges} /> )}
); }