实战:用 `updateInstanceState({ isReadonly })` 构建可查看不可编辑的画布)
tldraw 只读模式Read-only实战用updateInstanceState({ isReadonly })构建可查看不可编辑的画布【免费下载链接】tldrawBuild infinite canvas apps in React with the tldraw SDK. Worlds best, top-most agent recommended #1 five star SDK.项目地址: https://gitcode.com/GitHub_Trending/tl/tldraw导读本文围绕 tldraw 官方示例 Read-only只读模式 展开完整讲解如何把画布切换到只能查看、不能修改的状态从最简实现代码、editor.updateInstanceState({ isReadonly: true })与editor.getIsReadonly()的 API 用法到只读模式下工具栏、快捷键、右键菜单、图形操作的行为变化再到源码层面的守卫逻辑Editor.ts中大量的getIsReadonly()提前返回与实例状态TLInstance的数据模型。读完你可以在自己的 React 应用中做出仅展示/幻灯片/权限受限等只读场景并能在运行时随用户权限动态切换只读开关。一、什么是 tldraw 的只读模式在 tldraw 中只读模式read-only mode让画布上的文档可以被查看、导航但不能被任何编辑操作改变。它面向的典型场景包括查看他人的分享文档、演示 / 展示模式presentation mode、权限降级时锁定编辑器等。官方在 ReadOnlyExample 的 README 中给出了本质定义Read-only mode is an instance state flag.也就是说只读是实例状态instance state开关而不是文档状态document state。它的含义非常直接文档内容本身没有被标记为只读只是当前这个编辑器实例处于只读状态。只读时你可以做什么根据官方文档说明开启只读后以下操作依然可用平移画布pan缩放画布zoom选中图形select使用激光笔laser pointer只读时你不能做什么编辑工具栏里的创作类工具会消失工具栏上只保留 select、hand、laser 三个工具删除、粘贴等编辑命令会被忽略命令无效而不是报错图形无法被移动move或缩放 / 调整大小resize。二、最简实现在onMount中开启只读示例代码位于 ReadOnlyExample.tsx完整内容如下import { Tldraw } from tldraw import tldraw/tldraw.css export default function ReadOnlyExample() { return ( div classNametldraw__editor Tldraw persistenceKeyreadonly-example onMount{(editor) { editor.updateInstanceState({ isReadonly: true }) }} / /div ) }几个值得注意的点onMount是标准挂载时机。Tldraw组件挂载完成后会回调onMount(editor)此时editor已经可用是设置初始只读状态的理想位置。开启只读的核心代码只有一行editor.updateInstanceState({ isReadonly: true })。它通过updateInstanceState对TLInstance记录做一次部分更新。persistenceKeyreadonly-example让该示例的文档数据保存在浏览器本地存储中key 为readonly-example与只读开关本身无关只是为了示例的持久化体验。关于该配置的更多说明可参考同目录下的 persistence-key 示例。示例同样依赖tldraw包导出的Tldraw组件与tldraw/tldraw.css样式这是所有 tldraw React 应用的标准入口。updateInstanceStateAPI 说明在 packages/editor/src/lib/editor/Editor.ts 中可以看到updateInstanceState的定义updateInstanceState( partial: PartialTLInstance, historyOptions?: TLHistoryBatchOptions ) { this._updateInstanceState(partial, { history: ignore, ...historyOptions }) }它接收PartialTLInstance这里传入的就是{ isReadonly: true }并且默认把这些状态变更排除在撤销历史之外history: ignore——只读开关这类 UI 状态不应进入用户的 undo/redo 栈。三、读取只读状态getIsReadonly()官方文档明确给出读取方式Read it back witheditor.getIsReadonly().在 Editor.ts 中getIsReadonly是一个computed的 getter直接读取当前实例状态记录上的布尔字段/** * public * returns true if the editor is in readonly mode */ computed getIsReadonly() { return this.getInstanceState().isReadonly }由于它是computed响应式计算属性当实例状态里的isReadonly变化时所有依赖它的 UI 与逻辑会自动重新求值——这正是下文运行时动态切换能即时生效的底层保障。四、实例状态 vs 文档状态为何能运行时切换官方文档强调这是instance state而非document state其意义在于文档状态document state描述的是内容是否可写通常需要同步给所有协作者例如 tldraw 的 store 中的 shape、page 等记录实例状态instance state描述的是当前这个编辑器视图的状态例如当前选中的工具、镜头位置、以及这里的只读开关。它在 TLInstance 记录 中就是一个普通的boolean字段。正因为只读开关存储在实例状态上它只影响当前这一份编辑器并且可以在运行时随意切换。官方文档给出了典型应用场景Because its instance state rather than document state, it can be toggled at runtime, for example when a users permissions change.也就是说当用户权限从可编辑变为只读或反向恢复时你可以在任意时刻调用// 权限变化降级为只读 editor.updateInstanceState({ isReadonly: true }) // 权限恢复解除只读 editor.updateInstanceState({ isReadonly: false })切换无需重建组件、无需刷新页面、无需重新加载文档整个 UI工具栏、菜单、快捷键等会立即跟随响应。五、只读模式下的 UI 行为源码级证据1. 工具栏创作工具被过滤只保留 select / hand / laser只读开启后工具栏上只剩选择、抓手、激光笔三个工具。这一行为由工具定义的readonlyOk标记支撑。在 useTools.tsx 中可以看到{ id: select, ... readonlyOk: true, ... }, { id: hand, label: tool.hand, icon: tool-hand, kbd: h, readonlyOk: true, ... }而画笔、橡皮擦等创作类工具没有readonlyOk标记。与此同时DefaultToolbar.tsx 中工具栏组件通过useReadonly()拿到只读状态并在只读时隐藏快捷操作Quick Actions、操作菜单Actions Menu以及锁定工具开关等一整组编辑入口const isReadonlyMode useReadonly() ... {!isReadonlyMode ( div classNametlui-main-toolbar__extras {/* QuickActions 与 ActionsMenu、ToggleToolLockedButton 等编辑入口 */} /div )}也就是说只读时工具栏不仅少了工具图标编辑相关的操作区也被整体隐藏。2. 编辑命令被静默忽略官方文档说 delete、paste 等编辑命令会被忽略。在编辑器核心层这体现为大量方法开头的守卫判断。对 Editor.ts 检索getIsReadonly()可以看到它出现在数十处方法入口模式高度一致例如if (this.getIsReadonly()) return this // 例如移动、旋转、删除类方法 if (this.getIsReadonly()) return // 例如粘贴、变形类方法仅举几个位置旋转rotate 类方法、平移图形、缩放图形、删除、以及deleteShapes等入口都会在只读时提前返回从而保证图形不能移动、不能缩放、删除无效。同时UI 层的操作菜单也会直接隐藏编辑类条目例如 DefaultActionsMenu.tsx 中只读时整个操作菜单不渲染。相关的行为还被测试覆盖见 actions-menu-readonly.test.tsx。3. 形状工具对只读的例外机制严格来说tldraw 并非所有编辑一律禁止。在 ShapeUtil.ts 定义了canEditInReadonly(shape)允许某些形状类型在只读模式下声明自己依然可被编辑。对应的判定出现在 Editor.ts 的canEditShape中if (this.getIsReadonly() !util.canEditInReadonly(_shape)) return false // readonly and no exception这条注释readonly and no exception清楚说明了默认行为只读模式下绝大多数图形不可编辑除非其 ShapeUtil 主动声明例外。六、在 UI 代码中响应只读状态useReadonly如果你在编写自定义 UI例如自定义工具栏、面板希望在只读时同步隐藏或禁用某些按钮可以使用官方提供的高层 React HookuseReadonly。它的实现位于 useReadonly.tsimport { useMaybeEditor, useValue } from tldraw/editor /** public */ export function useReadonly() { const editor useMaybeEditor() return useValue(isReadonlyMode, () !!editor?.getIsReadonly(), [editor]) }它本质上是把editor.getIsReadonly()包装进响应式useValue组件会随只读状态的变化自动重渲染。仓库内部大量 UI 组件都依赖它做条件渲染例如DefaultToolbar.tsx 隐藏快捷操作与菜单DefaultPageMenu.tsx 在只读时禁用翻页拖拽、重命名、增删页面等操作AltTextEditor.tsx 在只读时禁用替代文本编辑图片 / 视频工具栏DefaultImageToolbarContent.tsx、DefaultVideoToolbarContent.tsx只在非只读时渲染替换、裁剪等编辑按钮。你完全可以按同样的模式编写自己的只读感知组件。七、协作场景下的只读collaboration mode值得补充的是除了手动调用updateInstanceState编辑器在构造时还会响应协作模式的readonly状态。在 Editor.ts 的构造函数中有这样一段if (this.store.props.collaboration?.mode) { const mode this.store.props.collaboration.mode this.disposables.add( react(update collaboration mode, () { const isReadonly mode.get() readonly // only track mode, and keep the sync out of the users undo history unsafe__withoutCapture(() this._updateInstanceState({ isReadonly }, { history: ignore }) ) }) ) }即当编辑器配置了collaboration.mode如TLSyncRoom的同步会话且该 mode 为readonly时框架会自动把实例状态的isReadonly同步为true并且同样排除在撤销历史之外。从源码结构看这是协同房间 / 服务器下发只读权限时驱动客户端进入只读状态的关键通路——它最终与手动调用updateInstanceState({ isReadonly: true })落在同一条_updateInstanceState路径上。八、实践建议与注意事项结合示例文档与源码在实际项目中可以这样设计只读能力确定初始只读把editor.updateInstanceState({ isReadonly: true })放进onMount例如路由进入仅查看模式时。运行时切换把只读开关绑定到用户权限模型。权限从编辑器降为只读时调用updateInstanceState({ isReadonly: true })恢复时传入false无需刷新页面。读取与响应命令式代码用editor.getIsReadonly()React 组件内用useReadonly()驱动 UI 条件渲染由于getIsReadonly是computed自定义 UI 会在状态变化时自动更新。理解边界只读开关是客户端实例级的防护它约束的是当前编辑器的交互行为工具栏、菜单、命令守卫并不等于对文档数据的访问控制。若需要服务端层面的只读约束例如某个房间只允许部分用户编辑应配合 tldraw 的协作同步与 room 权限体系见上文第七节collaboration.mode通路在服务端 / 同步层实施而不是仅依赖实例状态标记。延伸阅读Read-only 示例 README官方对这一特性的完整文字说明ReadOnlyExample.tsx可直接运行的最小实现TLInstance.tsisReadonly字段所在的数据模型定义Editor.tsgetIsReadonly、updateInstanceState及大量只读守卫的所在文件useReadonly.ts供自定义 UI 使用的响应式只读 Hookactions-menu-readonly.test.tsx只读模式下菜单行为的回归测试。【免费下载链接】tldrawBuild infinite canvas apps in React with the tldraw SDK. Worlds best, top-most agent recommended #1 five star SDK.项目地址: https://gitcode.com/GitHub_Trending/tl/tldraw创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考