
如何用 ExcalidrawElementSkeleton 与 convertToExcalidrawElements 以代码方式创建 Excalidraw 元素【免费下载链接】excalidrawVirtual whiteboard for sketching hand-drawn like diagrams项目地址: https://gitcode.com/GitHub_Trending/ex/excalidraw如果你的项目把 Excalidraw 作为嵌入式白板组件使用并需要在代码中动态生成画布内容——例如根据接口数据自动绘制流程图、在用户点击按钮时往画布追加一组形状和箭头——那么手写完整的ExcalidrawElement对象会非常繁琐每个元素都有id、version、seed、boundElements等几十个字段。Excalidraw 提供了一条简化路径用只包含最少属性的ExcalidrawElementSkeleton描述元素再通过convertToExcalidrawElements转换成完整元素交给initialData或updateScene渲染到画布上。需要注意的是Skeleton API 目前仍处于beta 状态官方文档明确说明它在转为 stable 之前可能变化参见 Skeleton API 文档。下文所有用法均以该文档及其配套实现 transform.ts 为准。准备条件安装与导入按 安装文档用 npm 或 yarn 安装包npm install react react-dom excalidraw/excalidraw # 或 yarn add react react-dom excalidraw/excalidraw使用模块打包器如 Webpack时按 ES6 模块导入即可参见 集成文档import { Excalidraw } from excalidraw/excalidraw; import { convertToExcalidrawElements } from excalidraw/excalidraw;如果项目使用 Next.jsExcalidraw 不支持服务端渲染必须只在客户端渲染。集成文档给出的做法是用next/dynamic动态导入并设置ssr: false当需要同时导入convertToExcalidrawElements这类工具函数而非Excalidraw组件本身时动态导入命名导出的方式不生效文档建议写一个 wrapper 组件再整体动态导入app router 下还需在文件顶部加use client指令use client; import { Excalidraw, convertToExcalidrawElements } from excalidraw/excalidraw; import excalidraw/excalidraw/index.css;convertToExcalidrawElements 的签名与参数函数签名来自 Skeleton 文档convertToExcalidrawElements( elements: ExcalidrawElementSkeleton, opts?: { regenerateIds: boolean } ): ExcalidrawElement[]两个参数需要注意参数类型默认值说明elementsExcalidrawElementSkeleton待转换的元素 Skeleton。文档中的全部示例都以数组形式传入opts.regenerateIdsbooleantrue默认会为所有元素重新生成id即使你传了id也会覆盖。如果希望保留自己指定的id需显式传{ regenerateIds: false }实现位于 transform.ts当opts.regenerateIds ! false时对每个元素执行Object.assign(element, { id: randomId() })这与文档描述的默认行为一致。关键前提Skeleton 本身不能直接渲染。文档明确写道convertToExcalidrawElements必须在把元素传给initialData、updateScene等 API 之前调用转换得到的ExcalidrawElement[]才能被画布渲染。各类型 Skeleton 的必填字段与写法基本图形rectangle、ellipse、diamond只需type、x、y三个必填属性其余样式属性可选。不传width/height时使用默认尺寸可附加backgroundColor、strokeColor、strokeStyle、fillStyle、strokeWidth等属性装饰形状文档示例值可整体替换后运行convertToExcalidrawElements([ { type: rectangle, x: 50, y: 250, width: 200, height: 100, backgroundColor: #c0eb75, strokeWidth: 2, }, { type: ellipse, x: 300, y: 250, width: 200, height: 100, backgroundColor: #ffc9c9, strokeStyle: dotted, fillStyle: solid, strokeWidth: 2, }, { type: diamond, x: 550, y: 250, width: 200, height: 100, backgroundColor: #a5d8ff, strokeColor: #1971c2, strokeStyle: dashed, fillStyle: cross-hatch, strokeWidth: 2, }, ]);文本元素type、x、y、text四项必填fontSize、strokeColor等可选convertToExcalidrawElements([ { type: text, x: 100, y: 100, text: HELLO WORLD!, }, { type: text, x: 100, y: 150, text: STYLED HELLO WORLD!, fontSize: 20, strokeColor: #5f3dc4, }, ]);线段与箭头typeline或arrow、x、y必填。可附加startArrowhead、endArrowhead、strokeColor、strokeWidth、strokeStyle等convertToExcalidrawElements([ { type: arrow, x: 450, y: 20, startArrowhead: circle, endArrowhead: triangle, strokeColor: #1971c2, strokeWidth: 2, }, { type: line, x: 450, y: 60, strokeColor: #2f9e44, strokeWidth: 2, strokeStyle: dotted, }, ]);文本容器带 label 的形状在type、x、y之外必须提供label属性且label.text必填label内的strokeColor、fontSize、textAlign、verticalAlign等可选。文档说明如果不提供容器尺寸会根据 label 的尺寸计算容器大小。convertToExcalidrawElements([ { type: rectangle, x: 300, y: 290, label: { text: RECTANGLE TEXT CONTAINER, }, }, { type: ellipse, x: 500, y: 100, label: { text: ELLIPSE\n TEXT CONTAINER, }, }, ]);带标签的箭头与箭头绑定箭头同样支持label。更进一步通过start/end属性可以把箭头绑定到形状或文本上start和end中传type或id之一即可。当start/end没有给出坐标时会按箭头自身位置计算绑定元素的位置。文档中的箭头绑定示例整体可运行值来自文档convertToExcalidrawElements([ { type: ellipse, id: ellipse-1, strokeColor: #66a80f, x: 390, y: 356, width: 150, height: 150, backgroundColor: #d8f5a2, }, { type: diamond, id: diamond-1, strokeColor: #9c36b5, width: 100, x: -30, y: 380, }, { type: arrow, x: 100, y: 440, width: 295, height: 35, strokeColor: #1864ab, start: { type: rectangle, width: 150, height: 150, }, end: { id: ellipse-1, }, }, { type: arrow, x: 60, y: 420, width: 330, strokeColor: #e67700, start: { id: diamond-1, }, end: { id: ellipse-1, }, }, ]);这里的要点多条箭头引用同一个已有元素时用id绑定。因为默认regenerateIds: true你写的id会被重新生成实现内部会维护旧 id 到新 id 的映射oldToNewElementIdMap自动把start/end里的旧 id 改写为新生成的 id所以按文档这样写即可直接运行只有在你显式传{ regenerateIds: false }复用既有元素时才需要保证id与元素自身一致。创建 frameframe 的必填属性是type: frame和children成员元素 id 的数组name可选convertToExcalidrawElements([ { type: rectangle, x: 10, y: 10, strokeWidth: 2, id: 1 }, { type: diamond, x: 120, y: 20, backgroundColor: #fff3bf, strokeWidth: 2, label: { text: HELLO EXCALIDRAW, strokeColor: #099268, fontSize: 30 }, id: 2 }, { type: frame, children: [1, 2], name: My frame } ]);从 实现代码 可以看到frame 会在所有子元素处理完之后统一处理children引用的 id 会被解析为实际元素frame 的坐标和尺寸默认根据子元素的外包边界自动计算含 padding。把转换结果交给 Excalidraw 渲染路径一挂载时通过 initialData 加载convertToExcalidrawElements的返回值直接放进 initialData 的elements字段。initialData支持elements、appState、scrollToContent等字段scrollToContent: true会在挂载后自动滚动到元素使其居中。Skeleton 文档给出的完整示例function App() { const elements convertToExcalidrawElements([ { type: rectangle, x: 100, y: 250, }, { type: ellipse, x: 250, y: 250, }, { type: diamond, x: 380, y: 250, }, ]); return ( div style{{ height: 500px }} Excalidraw initialData{{ elements, appState: { zenModeEnabled: true, viewBackgroundColor: #a5d8ff }, scrollToContent: true, }} / /div ); }注意容器要有非零尺寸——Excalidraw 占满外层容器的宽高见 安装文档 中 “Dimensions of Excalidraw” 一节。路径二运行中通过 excalidrawAPI.updateScene 更新如果画布已挂载、需要动态更新场景先通过excalidrawAPI回调拿到 API 并存入 state参见 Excalidraw API 文档function App() { const [excalidrawAPI, setExcalidrawAPI] useState(null); return ( div style{{ height: 500px }} Excalidraw excalidrawAPI{(api) setExcalidrawAPI(api)} / /div ); }之后调用updateScene参数结构含elements、appState、captureUpdate等字段。用 Skeleton 生成元素时同样是先转换再传入const sceneData { elements: convertToExcalidrawElements([ { type: rectangle, x: 100, y: 100 }, { type: text, x: 120, y: 120, text: NEW ELEMENT }, ]), appState: { viewBackgroundColor: #edf2ff }, }; excalidrawAPI.updateScene(sceneData);文档提示Ref支持已在 v0.17.0 移除必须改用excalidrawAPI方式访问 API。验证结果与常见报错文档给出的验证方式是渲染结果把转换后的元素传给initialData或updateScene后画布上应出现对应的形状、文本、箭头绑定和 frame。仓库内也提供了对应的单元覆盖transform.test.ts 对convertToExcalidrawElements的各类输入做了行为断言可作为转换行为的参照。排查时可以关注实现里明确存在的几类控制台报错transform.tsNo element for start binding with id {id} found/No element for end binding with id {id} found箭头start/end中通过id绑定时找不到对应元素——检查id是否拼错、是否在传入的 Skeleton 数组中定义过Duplicate id found for {id}同一次转换中出现重复id在regenerateIds: false时更容易触发Element with {id} wasnt mapped correctly/Excalidraw element with id {id} doesnt existframe 的children引用了不存在或未正确映射的元素 id文本绑定缺少text时会报No text found for start/end binding text element。边界与限制该 API 为 beta签名与行为在 stable 之前可能调整升级excalidraw/excalidraw后建议回归验证一次转换结果。regenerateIds默认true不显式设false时自己写的id会被覆盖跨渲染周期复用同一批元素时需注意这一点。箭头绑定中start/end支持以type内联声明被绑定元素此时该元素由转换过程生成也支持以id引用数组内已有元素文档示例覆盖rectangle、ellipse、diamond、text这几类可绑定类型。完成以上步骤后你就可以只写最少属性在代码中生成 Excalidraw 元素并通过initialData或updateScene验证它们出现在画布上。【免费下载链接】excalidrawVirtual whiteboard for sketching hand-drawn like diagrams项目地址: https://gitcode.com/GitHub_Trending/ex/excalidraw创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考