新闻详情

新闻详情

首页 / 资讯中心 / 详情

OpenPencil Vue SDK:usePropScrub 拖动式属性更新与 Undo 感知提交机制

发布时间:2026/9/25 4:18:47来源:尧图网络
OpenPencil Vue SDK:usePropScrub 拖动式属性更新与 Undo 感知提交机制
前端桌面应用AI 应用MCP 服务【免费下载链接】open-pencilAI-native design editor. Open-source Figma alternative.项目地址https://gitcode.com/gh_mirrors/op/open-pencil点击查看免费下载本文围绕 OpenPencil 的usePropScrubComposable 展开介绍它如何在 Vue 属性面板中实现拖动即预览、松手才提交的数值属性编辑updateProp在拖动过程中实时刷新画布上的节点属性交互结束后commitProp将最终值写入 Undo 历史cancelProp则可整体回滚。读完本文你可以基于该 API 自定义数字输入控件并理解其底层的节点预览NodePreview与撤销历史衔接机制。一、API 概览usePropScrub(editor)usePropScrub(editor)接收一个编辑器实例Editor类型来自open-pencil/core/editor协调scrubbing按住拖动连续调节数值期间的实时属性更新并在交互结束时提交一个 Undo 感知的变更。它适用于构建通过拖动直接修改选中节点数值属性如 x/y、width/height、rotation 等的数字控件。源码实现位于 prop-scrub/use.tsimport type { Editor } from open-pencil/core/editor import type { NumericNodeProperty, SceneNode } from open-pencil/scene-graph import { useNodePreview } from #vue/controls/node-preview/use export function usePropScrub(editor: Editor) { const preview useNodePreview(editor) function updateProp(nodes: SceneNode[], key: NumericNodeProperty, value: number) { preview.update( nodes.map((node) node.id), { [key]: value }, Change ${key} ) } function commitProp( _nodes: SceneNode[], _key: NumericNodeProperty, _value: number, _previous: number ) { preview.commit() } function cancelProp(_nodes: SceneNode[], _key: NumericNodeProperty) { preview.cancel() } return { updateProp, commitProp, cancelProp } }可以看到usePropScrub本身是一个薄封装它把三个方法全部委托给 node-preview/use.ts 中的useNodePreview。三个方法的参数说明方法参数说明updateProp(nodes, key, value)nodes: SceneNode[]要更新的节点数组支持多选场景下对多个节点同时赋值key: NumericNodeProperty场景图的数值属性键例如x、width、rotationvalue: number拖动过程中写入的当前数值commitProp(nodes, key, value, previous)四个参数均为SceneNode[]/ 属性键 / 当前值 / 拖动前的原值实现中仅调用preview.commit()参数带_前缀表示未使用保留完整签名是为了与控件层的提交回调约定保持一致cancelProp(nodes, key)节点数组与属性键实现中仅调用preview.cancel()整体回滚未提交的预览值得注意的两点Undo 标签由updateProp内的Change ${key}生成即撤销面板中显示的操作名会带上属性名如 Change width首次updateProp调用时才会通过editor.beginNodePreview(label)开启一次预览会话后续调用复用同一次会话保证整段拖动只产生一条撤销记录。二、中间层useNodePreview 如何固定编辑目标useNodePreview 是usePropScrub与编辑器核心之间的桥接层其注释写明了设计意图Keeps an edits original targets until its terminal event, even after selection changes——即使选择发生变化也保留本次编辑最初的编辑目标直到收到终止事件提交或取消。关键实现export function useNodePreview(editor: Editor) { let preview: NodePreview | undefined let targets: readonly string[] [] function update(ids: readonly string[], changes: PartialSceneNode, label: string) { if (!preview) { if (!ids.length) return targets [...ids] preview editor.beginNodePreview(label) } try { for (const id of targets) preview.update(id, changes) } catch (error) { cancel() throw error } } // takePreview / commit / cancel 略 tryOnScopeDispose(cancel) if (getCurrentInstance()) onDeactivated(cancel) return { update, commit, cancel } }这段代码解决了一个典型的 UI 交互隐患预览创建后update始终作用于首次记录的targetsfor (const id of targets)不会因为中途改变选择而把值写错到别的节点上通过tryOnScopeDispose(cancel)与onDeactivated(cancel)注册兜底取消当组件被销毁或从keep-alive中停用时未提交的预览会被自动回滚不会把脏数据留在场景图上。三、底层机制NodePreview 与撤销历史的衔接真正做脏值跟踪与 Undo 写入的是编辑器核心的 node-preview.ts。createNodePreviewActions导出的NodePreview接口只有四个成员export interface NodePreview { readonly closed: boolean update: (id: string, changes: PartialSceneNode) void commit: () void cancel: () void }其内部工作流可以概括为四步捕获原值capture。update(id, changes)在写入前先通过capture把该节点尚未记录过的字段做一次structuredClone快照存入originals。同时处于graph.isApplyingLayout过程中被改动的节点会被标记为layoutOnly即只是布局联动产生的衍生变更为提交时的回放方式做准备。实时预览写入。真正的属性写入走graph.runPreviewUpdates(() updateNode(id, changes), capture)属于预览性质的更新画布会重绘ctx.requestRepaint()但不会触发已提交变更的对外事件也不会进入 Undo 栈。提交commit。先对所有目标节点assertNodeEditable然后为每个节点生成{ id, before, after }编辑对before取自快照、after取自当前节点值仅保留被改动的键。若before与after相等则跳过。最后ctx.undo.push({ label, forward: () apply(after), inverse: () apply(before) }) apply(after)这一步把整次拖动压缩成一条带前后镜像的 Undo 记录——无论拖动过程中调用过多少次updatePropCtrlZ一次即可回到拖动前的状态CtrlShiftZ一次即可恢复。layoutOnly节点在回放时使用graph.withLayoutMutations包裹保证布局联动变更被正确重放。取消cancel。用originals快照逐节点执行graph.updateNodePreview(id, previous)还原原值且不向 Undo 栈写入任何记录注释明确cancellation publishes no committed node edits。此外beginNodePreview会为预览订阅几个编辑器事件selection:changed、page:changed、graph:replaced直接触发preview.cancel()node:deleted在删除的节点属于编辑目标时同样取消。这意味着一旦用户在拖动期间改变了选择或切换了页面进行中的预览会被安全回滚防止数值串到其他节点上。四、实战用法在属性面板中接住一次拖动1. 通过 usePosition 直接调用仓库中 position/use.ts 给出了标准接法usePosition()内部组合了useNodeProps()提供当前选中的nodes与usePropScrub(editor)再对外暴露收窄了上下文的三个方法const { nodes } useNodeProps() const { updateProp: _updateProp, commitProp: _commitProp, cancelProp: _cancelProp } usePropScrub(editor) function updateProp(key: NumericNodeProperty, value: number) { _updateProp(nodes.value, key, value) } function commitProp(key: NumericNodeProperty, value: number, previous: number) { _commitProp(nodes.value, key, value, previous) }2. 通过 PositionControls 原语消费 actionsPositionControlsRoot.vue 把usePosition()的updateProp / commitProp / cancelProp打包成actions通过插槽下发给自定义控件PositionControlsRoot template #default{ xValue, wValue, hValue, actions } NumberField v-modelxValue labelX updateactions.updateProp(x, $event) commitactions.commitProp(x, $event.value, $event.previous) cancelactions.cancelProp(x) / /template /PositionControlsRoot这套原语组件提供状态与 actions插槽内控件负责交互事件的分层使得NumberField这类输入控件可以完全复用同一套预览/提交管线。3. 完整交互时序一次典型的拖动调节可归纳为用户按下并拖动 NumberField └─ 每帧: updateProp(nodes, width, v) // 预览写入画布实时重绘不进 Undo 用户松开交互结束 └─ commitProp(nodes, width, v, v0) // 快照对比 → ctx.undo.push → 单条 Undo 记录 或 用户按 Esc / 选择变化 / 组件销毁 └─ cancelProp(nodes, width) // 按快照还原不产生任何提交记录五、测试佐证目标固定、选择变化自动取消、作用域销毁自动回滚tests/engine/vue/controls/property-preview.test.ts 的 pins targets through selection changes and releases previews on scope disposal 用例覆盖了这套机制的三个关键行为const scrub scope.run(() usePropScrub(editor)) scrub.updateProp([first], x, 50) // 开启预览first.x 预览为 50 editor.select([second.id]) // 选择变化 → 预览被自动 cancelfirst.x 还原为 10 scrub.updateProp([second], x, 80) // 旧预览已 closed更新成为 no-op scrub.commitProp([second], x, 80, 30) // closed 预览的 commit 不产生 Undo expect([first.x, second.x]).toEqual([10, 30]) expect(editor.undo.canUndo).toBe(false) // 确认没有脏的 Undo 记录 scrub.updateProp([second], x, 90) scope.stop() // 作用域销毁 → 未提交预览被自动回滚 expect(second.x).toBe(30) expect(editor.isInteractiveEditing()).toBe(false)该用例印证了前文两个结论选择变化会触发核心层自动取消预览作用域销毁组件卸载/停用的兜底保证预览一定被释放且interactive editing状态随之结束。同文件后续的 size/layout 用例还验证了预览会话能拥有拖动过程中派生的隐式变更例如 HUG 尺寸被拖成 FIXED 的转换提交或取消时一并生效/回滚。六、边界与注意事项一次拖动 一条 Undo 记录。多次updateProp不会累积多条撤销项只有commitProp时before/after有差异才会真正ctx.undo.pushedits.length 0时仅请求重绘。编辑目标在预览开始时即固定。中途改变选择不会把值写到新选中的节点而是直接取消本次预览——自定义控件若希望换目标后继续拖需要重新走一次updateProp开启新预览。不可编辑节点会中断预览。update与commit内部都会assertNodeEditable失败时自动cancel()后重新抛出异常调用方无需额外处理。参数签名与实现的分离。commitProp/cancelProp的参数在实现中未使用源码中以_前缀标注但控件层回调约定仍按(nodes, key, value, previous)传递保持了对上层控件的统一接口。相关 APINumberField数字输入控件原语usePropScrub的典型搭配对象参见 advanced 文档索引 中的 NumberField 条目useNodeProps提供当前选中节点集合的 Composable通常与usePropScrub组合使用node-preview/use.ts 与 node-preview.ts预览/提交/取消管线的完整实现packages/vue/src/index.tsopen-pencil/vue的公开导出入口usePropScrub等 Composable 从该包对外提供。赞分享前端桌面应用AI 应用MCP 服务【免费下载链接】open-pencilAI-native design editor. Open-source Figma alternative.项目地址https://gitcode.com/gh_mirrors/op/open-pencil点击查看免费下载相关推荐aider 的 Git 集成机制详解自动提交、/undo 撤销与提交归属配置Qwen3-Coder 评估工具链实战aider 的 Git 集成机制详解自动提交、/undo 撤销与提交归属配置Qwen3 Coder 评估工具链实战 本指南以 aider 的官方 Git大模型代码模型微调模型评测强化学习open-pencil SDK 深度解析useNodeProps 属性面板 Composable 的混合值检测、多选更新与撤销感知提交open pencil SDK 深度解析useNodeProps 属性面板 Composable 的混合值检测、多选更新与撤销感知提交 useNodeProp前端桌面应用AI 应用MCP 服务深入 OpenPencil Vue SDK 的 Locale API低层国际化状态与语言切换机制深入 OpenPencil Vue SDK 的 Locale API低层国际化状态与语言切换机制 本文基于 OpenPencil 官方文档 locale ap前端桌面应用AI 应用MCP 服务上一篇radiobox.css完全指南从安装到自定义解锁纯CSS动画新技能下一篇Money Ruby库类型安全使用RBS签名提升代码质量的终极指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

节后康复学习日志:肩关节复合体与SOAP记录的7小时高效实践 2026/9/25 4:56:50

节后康复学习日志:肩关节复合体与SOAP记录的7小时高效实践

春节回来第一周,我最怕的不是肠胃,是书桌。Day1坐在桌前两个小时,光是翻目录就翻了四十分钟,脑子里全是年夜饭的油香和亲戚家小孩的哭声。到了Day2,我干脆不跟生物钟较劲了,把这一天的康复学习定在12:30到2…

阅读更多 →
Python+微信测试号实现每日天气自动推送:从API调用到定时任务 2026/9/25 4:56:44

Python+微信测试号实现每日天气自动推送:从API调用到定时任务

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Navicat免安装版深度解析:依赖库、配置与MySQL连接排查指南 2026/9/25 4:56:44

Navicat免安装版深度解析:依赖库、配置与MySQL连接排查指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
从能跑到敢上线:Agent Skill 质量三道门槛与测试上线全流程 2026/9/25 4:56:44

从能跑到敢上线:Agent Skill 质量三道门槛与测试上线全流程

1. 从"能跑"到"敢上线":Skill 质量的三道门槛写 Skill 这件事,门槛其实比大多数人想象的要低。一个SKILL.md加几个脚本,跑通一次 Demo,看起来就"成了"。但我自己踩过的坑告诉我:能跑通的…

阅读更多 →
HDFS基本操作本质:理解NameNode与DataNode协同机制 2026/9/25 4:56:43

HDFS基本操作本质:理解NameNode与DataNode协同机制

1. 为什么“HDFS基本操作”不是命令背诵,而是理解分布式文件系统的第一道门槛刚接触Hadoop生态时,我带过一批实习生,他们花两小时把hdfs dfs -ls /、-mkdir、-put这些命令抄在小本子上,信心满满地去跑第一个任务——结果卡在-put上…

阅读更多 →
中国移动H10G-13融合网关刷机攻略:晶晨S905L3芯片刷安卓9完整教程 2026/9/25 4:56:43

中国移动H10G-13融合网关刷机攻略:晶晨S905L3芯片刷安卓9完整教程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉