ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于 @open-pencil/vue 打造自定义设计编辑器:OpenPencil Vue SDK 开发指南

基于 @open-pencil/vue 打造自定义设计编辑器:OpenPencil Vue SDK 开发指南 前端桌面应用AI 应用MCP 服务【免费下载链接】open-pencilAI-native design editor. Open-source Figma alternative.项目地址https://gitcode.com/gh_mirrors/op/open-pencil点击查看免费下载open-pencil/vue是 OpenPencil 对外提供的 Vue 前端 SDK它把框架无关的核心引擎open-pencil/core适配为 Vue 注入上下文、响应式 Composables 与无样式headless结构原语使开发者可以在自己的产品、内部工具或专用工作流编辑器中嵌入 OpenPencil 的画布与编辑能力。本文以官方 SDK 文档为主线结合仓库源码完整讲解 SDK 的定位、设计原则、两层级 API、安装配置、编辑器外壳/导航面板/属性面板的实战构建以及 v0.14.0 迁移要点。SDK 定位OpenPencil 不只是一款独立设计应用官方文档德语版入口、英文版入口开篇即点明open-pencil/vue存在的意义是让 OpenPencil 超越独立设计应用这一定位成为可以嵌入其他产品、内部工具和特定工作流编辑器的工具包toolkit。内置的 OpenPencil 应用只是该工具包的一种组合方式而 SDK 就是用来构建另一种组合的途径。SDK 提供的核心能力包括通过 Vue 依赖注入Dependency Injection提供的编辑器上下文基于CanvasKit的画布渲染覆盖**选择selection、命令commands、菜单menu、属性面板property-panel、变量variables**的 Composables无样式结构原语如PageListRoot、PropertyListRoot、ToolbarRoot内置 i18n 原语用于菜单、面板、对话框的本地化以及自定义语言选择器。不同产品和团队需要不同的编辑界面有时是完整的图形设计编辑器有时是嵌入在另一个应用中的紧凑画布有时是内部工作流工具、模板编辑器或者围绕窄场景构建的 AI 辅助编辑界面——SDK 正是支撑这些可能性的那一层。设计原则Headless FirstSDK 的四个设计原则来自 架构文档决定了它的 API 形态无样式优先Headless first只提供逻辑与结构不规定应用的视觉风格Composable 优先于无意义的包装当不存在需要协调的结构时直接用 Composable 即可不要包一层组件有意的公共 API稳定导出集中定义在 packages/vue/src/index.ts这是唯一需要关心的公共面框架感知Framework-aware在open-pencil/core之上做 Vue 集成而不是重复实现编辑器内核。在此基础上还有一条职责边界的实用判断法则如果一段逻辑能在另一个基于 OpenPencil 的应用中复用、且不携带当前应用的样式它就属于open-pencil/vue反之样式、布局外壳、路由、产品文件流、toast 与菜单等应用专属 UX 则属于你的应用。两层级 APIComposables 与 PrimitivesSDK 的公共 API 分为两个主要层级Composables提供编辑器状态与相关动作。只需要状态和动作时从 Composables 开始组件/Primitives定义有意义的 UI 结构。当你在开发可复用的编辑器界面构件时从 Primitives 开始。官方文档将 API 分为三个区域详见 API 参考组件ComponentsCanvasRoot、CanvasSurface、ToolbarRoot、PageListRoot、PropertyListRoot、LayerTreeRoot、ColorPickerRoot、NumberFieldRoot、SegmentedControlRoot等结构原语ComposablesuseEditor、useCanvas、useSelectionState、useEditorCommands、usePosition、useFillControls等高级 APIAdvanceduseCanvasContext、useLayerTree、useVariables、useNumberField、useToolbar、usePropScrub、toolCursor、extractImageFilesFromClipboard等底层工具。从 packages/vue/src/index.ts 的导出可以看到公共面既包括类型导出Editor、EditorState、EditorOptions、EditorEvents、Tool、EditorToolDef也包括createEditor、EDITOR_TOOLS、TOOL_SHORTCUTS的转发以及provideEditor/useEditor/EDITOR_KEY上下文三件套。这印证了文档强调的稳定的公共 API 集中导出、内部模块不直接对外的设计。架构上还要避免宽泛的 context-dump 插槽优先使用聚焦的 slot props 或直接使用 Composable而不是把巨大的v-slotctx负载传给子组件。受控原语如PropertyListRoot只发出语义化事件选择与撤销接线应放在 adapter 或控制型 Composable 中而非原语内部。三层心智模型与包结构上手 SDK 前先建立三层心智模型快速开始open-pencil/core—— 框架无关的编辑器引擎场景图、状态、撤销、渲染请求open-pencil/vue—— Vue Composables 与无样式原语你的应用 —— 样式、路由、文件流程、产品专属 UI。open-pencil/vue本身不持有编辑器模型它把核心编辑器适配为Vue 注入、响应式 Composables、无样式结构原语、画布与输入接线四类能力。包内按领域组织架构文档组件族Canvas/、ColorPicker/、FillPicker/、FontPicker/、GradientEditor/、LayerTree/、PageList/、PropertyList/、PropertySection/、SegmentedControl/、NumberField/、Toolbar/内含结构/无样式原语与局部辅助Controlscontrols/存放属性面板与编辑器控制型 Composables如usePosition、useLayout、useAppearance、useColorModel、useTypography、useExport、useFillControls、useStrokeControls、useEffectsControls、useNodeProps、usePropScrub、useEditorPropertyListVariablesVariablesEditor/存放变量领域 Composables 与状态接线Selectionselection/存放选择派生状态与能力Contextcontext/存放编辑器注入辅助EDITOR_KEY、provideEditor、useEditorInternalinternal/存放跨领域工具不视为首要的无样式原语。快速开始安装与最小可运行编辑器安装依赖bun add open-pencil/core open-pencil/scene-graph open-pencil/vue canvaskit-wasmSDK 位于本仓库 monorepo 中以open-pencil/vue发布。当前开发版本要求 Vue^3.5.41使用可选 CanvasKit peer 时要求canvaskit-wasm 0.41.1。使用更早版本时请检查所安装包的 peer 依赖要求。import { createEditor } from open-pencil/core/editor import { provideEditor, useCanvas } from open-pencil/vue第一步创建编辑器实例核心状态是框架中立的传入reactive状态可以让 Vue 控件观察到编辑器变化import { reactive } from vue import { createDefaultEditorState, createEditor } from open-pencil/core/editor import { SceneGraph } from open-pencil/scene-graph const graph new SceneGraph() const page graph.getPages()[0] if (!page) throw new Error(Expected an initial page) const editor createEditor({ graph, state: reactive(createDefaultEditorState(page.id)), getViewportSize: () ({ width: 1200, height: 800 }), })对照源码createEditor的EditorOptions完整字段定义在 packages/core/src/editor/types.ts选项类型说明graphSceneGraph场景图实例默认新建stateEditorState编辑器状态默认通过createDefaultEditorState生成loadFont(family, style, characters?, signal?) PromiseArrayBuffer \| null自定义字体加载resolveFigmaClipboardImagesFigmaClipboardImageResolverFigma 剪贴板图片解析器getViewportSize() { width: number; height: number }画布视口尺寸回调用于可调整大小的编辑器skipInitialGraphSetupboolean是否跳过初始场景图设置默认false。注意width和height不是EditorOptions的属性——视口尺寸必须通过getViewportSize返回。若编辑器可缩放应让该回调返回当前画布容器的实际尺寸不要包含侧边栏与工具栏。第二步把编辑器注入 Vue 子树script setup langts import { provideEditor } from open-pencil/vue import type { Editor } from open-pencil/core/editor const props defineProps{ editor: Editor }() provideEditor(props.editor) /script template slot / /template可以把这层看作编辑器组件树的 provider 层。文档推荐直接使用provideEditor()因为它是当前真实 API 面。第三步挂载画布script setup langts import { ref } from vue import { useCanvas, useEditor } from open-pencil/vue const canvasRef refHTMLCanvasElement | null(null) const editor useEditor() useCanvas(canvasRef, editor) /script template canvas refcanvasRef classsize-full / /template使用 Composables 读写状态编辑器注入后子组件即可读取选择并发出命令import { useEditorCommands, useSelectionState } from open-pencil/vue const selection useSelectionState() const commands useEditorCommands()完整最小示例script setup langts import { ref } from vue import { useCanvas, useEditor, useSelectionState } from open-pencil/vue const canvasRef refHTMLCanvasElement | null(null) const editor useEditor() const { selectedCount } useSelectionState() useCanvas(canvasRef, editor, { onReady: () { console.log(Canvas ready) }, }) /script template div classgrid h-full grid-rows-[1fr_auto] canvas refcanvasRef classsize-full / div classborder-t px-3 py-2 text-xs text-muted Selected: {{ selectedCount }} /div /div /templateuseCanvas支持onReady之类的回调选项useSelectionState返回的selectedCount是响应式的可直接渲染在状态栏。实战一构建自定义编辑器外壳官方指南 Custom Editor Shell 给出了一个典型的三层编辑器外壳open-pencil/core创建编辑器、open-pencil/vue适配为 Composables 与原语、你的应用负责外壳与产品 UX。推荐的组合方式是顶层用provideEditor()提供编辑器画布居中一侧放页面/图层导航另一侧放属性面板菜单与工具栏由 Composables 驱动。完整示例script setup langts import { reactive } from vue import { createDefaultEditorState, createEditor } from open-pencil/core/editor import { SceneGraph } from open-pencil/scene-graph import { provideEditor, CanvasRoot, CanvasSurface, ToolbarRoot, PageListRoot, } from open-pencil/vue const graph new SceneGraph() const page graph.getPages()[0] if (!page) throw new Error(Expected an initial page) const editor createEditor({ graph, state: reactive(createDefaultEditorState(page.id)), getViewportSize: () ({ width: 1440, height: 900 }), }) provideEditor(editor) /script template div classgrid h-screen grid-cols-[240px_1fr_320px] grid-rows-[48px_1fr] ToolbarRoot v-slot{ tools, activeTool, actions } header classcol-span-3 flex items-center gap-2 border-b px-3 button v-fortool in tools :keytool.key :data-activeactiveTool tool.key clickactions.setTool(tool.key) {{ tool.label }} /button /header /ToolbarRoot aside classborder-r PageListRoot v-slot{ pages, currentPageId, actions } nav button v-forpage in pages :keypage.id :data-activepage.id currentPageId clickactions.switch(page.id) {{ page.name }} /button /nav /PageListRoot /aside main CanvasRoot CanvasSurface classsize-full / /CanvasRoot /main aside classborder-l Properties panel here /aside /div /template示例中的固定视口尺寸是为了保持代码简短。在可调整大小的外壳中getViewportSize应返回画布容器本身的尺寸不要计入侧边栏和工具栏。这一分工之所以成立是因为SDK 拥有编辑器集成与可复用无样式逻辑你的应用拥有布局、样式与产品专属动作而 Composables 可以在没有额外包装组件的情况下驱动菜单与面板。实战二导航面板页面与图层OpenPencil 的侧边栏通常组合两个职责页面导航与图层导航Navigation Panels。页面导航使用PageListRoot或usePageList()PageListRoot v-slot{ pages, currentPageId, switchPage, addPage } div button v-forpage in pages :keypage.id clickswitchPage(page.id) {{ page.name }} /button button clickaddPage()New page/button /div /PageListRoot图层导航当你希望树结构由 SDK 管理、而展示由应用决定时使用LayerTreeRootLayerTreeRoot v-slot{ items, selectedIds, select, toggleExpand, getKey, getChildren } TreeView :itemsitems :selected-idsselectedIds :get-keygetKey :get-childrengetChildren selectselect toggle-expandtoggleExpand / /LayerTreeRoot常见的布局模式是页面列表在侧边栏顶部图层列表在下方行内重命名等细节控件内嵌在你的行组件中。LayerTreeRoot还配套buildLayerTreeModel、visibleLayerRows、useLayerTree等导出见 packages/vue/src/index.ts用于构建与虚拟化图层树模型。实战三属性面板Composable 优先属性面板在open-pencil/vue中刻意设计为composable-firstProperty Panels如果面板主要是选择派生值与更新动作优先使用 Composables如果需要可复用的数组/列表结构再使用PropertyListRoot之类的无样式原语。常用控制 Composables标准属性段从以下开始usePosition()useLayout()useAppearance()useTypography()useExport()列表型面板使用useFillControls()useStrokeControls()useEffectsControls()绑定感知字段BindableValueRoot对于可能引用变量或外部设计令牌的字段用BindableValueRoot包裹。该原语与展示无关但绑定感知接口应保持聚焦非破坏性字段闲置时展示变量身份在 tooltip 等辅助 UI 中暴露解析后的值聚焦或打开 picker 不得解除绑定只有用户真正改动值时才应用detach-on-edit、readonly-when-bound或edit-variable把显式的解绑动作放在 picker 中而不是放在破坏性的一键字段图标上将绑定替换、编辑时解绑与多目标变更放在同一个 provider 批次内。OpenPencil 自带应用在闲置时以紫色变量名 pill 展示绑定并在 NumberField 进入编辑模式时揭示解析后的数值自定义外壳可以用不同的方式呈现同一份无样式状态。绑定相关的底层 APIprovideBindingProvider、useBindingProvider、useOpenPencilBindingProvider、useNumberBindingProvider、useColorBindingProvider及BoundEditPolicy等类型同样导出自 packages/vue/src/index.ts。位置面板示例script setup langts import { usePosition } from open-pencil/vue const { x, y, width, height, updateProp, commitProp } usePosition() /script template div classgrid grid-cols-2 gap-2 input :valuex inputupdateProp(x, Number(($event.target as HTMLInputElement).value)) / input :valuey inputupdateProp(y, Number(($event.target as HTMLInputElement).value)) / input :valuewidth inputupdateProp(width, Number(($event.target as HTMLInputElement).value)) / input :valueheight inputupdateProp(height, Number(($event.target as HTMLInputElement).value)) / /div /template填充列表面板示例script setup langts import { PropertyListRoot, useEditorPropertyList, useFillControls } from open-pencil/vue const fillControls useFillControls() const fills useEditorPropertyList(fills) /script template PropertyListRoot prop-keyfills :itemsfills.items.value :mixedfills.isMixed.value addfills.actions.add removefills.actions.remove v-slot{ items, actions } div v-for(fill, index) in items :keyindex {{ fill.type }} button clickactions.remove(index)Remove/button /div button clickactions.add(fillControls.defaultFill)Add fill/button /PropertyListRoot /template经验法则直接的控件逻辑用 Composables重复的列表/树/插槽协调才是难点时用结构原语。从 v0.14.0 迁移的注意事项快速开始文档 末尾列出了当前开发版本相对 v0.14.0 的破坏性变更升级时需逐项处理场景图覆写Scene Graph overrides把SceneNode.overrides记录替换为instanceOverrides其self与descendants映射区分实例级与后代级覆写。应使用open-pencil/scene-graph公开的覆写辅助函数而不是当作简单的字段改名。派生几何Derived geometryfigmaDerivedLayout改名为derivedLayoutfigmaDerivedTextGlyphs改名为derivedTextGlyphs导出类型FigmaDerivedTextGlyph改名为DerivedTextGlyph。绑定提供者Binding providers实现getBindingId()并处理unresolved。edit-variable用prepareEdit()取代setValue()它捕获编辑键、值、setter 与恢复回调参见 BindableValue。翻译Translations用对应的产品领域 Composables 与目录取代useDialogMessages()和dialogMessages如useSettingsMessages()或useRenameMessages()。目录键也已移动不能只重命名导入参见 useI18n。CanvasKit使用PathBuilder进行可变构造不可变Path操作要保留返回的路径而不是期待原地修改。自定义工具Custom tools使用原生 Valibotinput模式与执行元数据取代params、ParamDef或paramToZod()。程序化 MCP 集成使用 MCP SDK v2 的 server/client 类型参见 MCP。API 分区与下一步SDK 的公共 API 按三个区域组织均有对应参考文档可查组件 ComponentsComposables高级 API Advanced继续深入推荐依次阅读SDK 架构architecture、useEditoruse-editor、useCanvasuse-canvas与useI18nuse-i18n。结合本仓库的 packages/vue/src 目录与 packages/core/src/editor 的createEditor/EditorOptions实现你可以在阅读文档的同时直接对照源码验证每个 API 的真实行为。赞分享前端桌面应用AI 应用MCP 服务【免费下载链接】open-pencilAI-native design editor. Open-source Figma alternative.项目地址https://gitcode.com/gh_mirrors/op/open-pencil点击查看免费下载相关推荐open-pencil open-pencil/vue Composables用 provideEditor、useEditor 与 useCanvas 构建自定义设计编辑器界面open pencil open pencil/vue Composables用 provideEditor、useEditor 与 useCanvas 构前端桌面应用AI 应用MCP 服务open-pencil open-pencil/vue 无头组件体系用无样式原语构建自定义设计编辑器界面open pencil open pencil/vue 无头组件体系用无样式原语构建自定义设计编辑器界面 本篇基于 open pencil 仓库中 pack前端桌面应用AI 应用MCP 服务OpenPencil Vue SDK Composables 全指南用可复用状态与动作构建自定义设计编辑器界面OpenPencil Vue SDK Composables 全指南用可复用状态与动作构建自定义设计编辑器界面 导读 open pencil/vue 是前端桌面应用AI 应用MCP 服务上一篇3行代码实现Excel数据透视表EasyExcel合并策略实战指南下一篇解决ComfyUI-Manager模块属性缺失的终极方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表