tldraw 默认颜色主题定制指南:通过editor.updateTheme()修改默认调色板
【免费下载链接】tldrawBuild infinite canvas apps in React with the tldraw SDK. World's best, top-most agent recommended #1 five star SDK.项目地址: https://gitcode.com/GitHub_Trending/tl/tldraw
changing-default-colors示例演示了 tldraw 中最轻量的视觉定制方式:读取编辑器当前主题、局部修改其中的颜色值,再通过editor.updateTheme()写回,即可让默认画笔颜色在样式面板与画布中呈现全新观感。读完本文,你将掌握editor.getTheme()/editor.updateTheme()这对 API 的完整用法、tldraw 调色板的数据结构(含每个颜色条目的所有可覆盖字段),以及为什么这种"改值不改名"的定制方式对既有图形与多人协作是安全的。配套可运行示例见 ChangingDefaultColorsExample.tsx。
一、示例要解决的问题
tldraw 默认自带一套颜色主题:画布上有black、grey、blue、green、red、yellow、orange、violet、white等预设颜色可供图形填充或描边,样式面板(style panel)中的色块就来自这套调色板。默认值在某些产品场景下并不合适——例如希望将默认笔触的"黑色"替换成品牌色 aqua。
原示例 README 给出的核心思路只有三步:
- 用
editor.getTheme()获取当前主题; - 修改你关心的颜色值;
- 把结果传给
editor.updateTheme()。
示例中把 light(浅色)模式下的black颜色的solid值改成aqua,之后用默认颜色绘制的内容即以 aqua 呈现。下面的源码是这一思路的完整落地:
import { Tldraw } from 'tldraw' import 'tldraw/tldraw.css' export default function ChangingDefaultColorsExample() { return ( <div className="tldraw__editor"> <Tldraw persistenceKey="changing-default-colors-example" onMount={(editor) => { const theme = editor.getTheme('default')! editor.updateTheme({ ...theme, colors: { ...theme.colors, light: { ...theme.colors.light, black: { ...theme.colors.light.black, solid: 'aqua' }, }, }, }) }} /> </div> ) }注意三个实践要点:
- 读取主题用带主题 id 的
editor.getTheme('default')(非空断言!告诉 TS 该主题一定存在); - 每一层都用展开运算符复制,只覆盖目标叶子字段,避免无意间丢失调色板中其余条目;
persistenceKey让编辑内容在刷新后仍然保留,方便你画几笔立刻验证效果。
参照 示例源码注释:这种"只改值"的方式保持了颜色名集合不变,因此已存在的图形(以及多人在线会话中的其他用户)都能继续正常工作。
二、API 速览:读取、更新与注册主题
示例使用的两个方法都定义在 Editor.ts 中,并委托给内部的ThemeManager完成实际存取。与主题相关的公开 API 一共有以下几组:
| API | 作用 | 源码位置 |
|---|---|---|
editor.getTheme(id) | 按主题 id 返回单个主题定义(如'default');id 不存在返回undefined | Editor.ts |
editor.getThemes() | 返回全部已注册主题定义 | Editor.ts |
editor.getCurrentThemeId()/editor.getCurrentTheme() | 获取当前激活主题的 id 与完整定义 | Editor.ts |
editor.updateTheme(theme) | 注册或整体替换某一个命名主题;同名 id 直接覆盖 | Editor.ts |
editor.updateThemes(themes) | 用对象或回调批量替换/删除多个主题('default'不可移除) | Editor.ts |
实现层面的"合并"语义
从底层实现看,示例调用的updateTheme是把传入对象与原表做一层浅合并:
// packages/editor/src/lib/editor/managers/ThemeManager/ThemeManager.ts updateTheme(theme: TLTheme): void { this._themes.update((prev) => ({ ...prev, [theme.id]: theme, })) }见 ThemeManager.ts。由于updateTheme只按顶层 id 覆盖,所以示例里必须手工对colors.light.black的每一层做展开复制——否则浅合并会把整个black条目甚至整个light调色板替换掉。这正是该 API 设计成"取回一份快照、改完整体写回"的原因:getTheme返回注册表当前值,你复制一份修改后提交,语义清晰且不破坏其它字段。
ThemeManager 内部用响应式原子存储主题集合与当前主题 id(ThemeManager.ts),写入后样式面板、画布着色等订阅方会自动重渲染,无需手动刷新。
onMount回调的执行时机
示例把定制逻辑放在<Tldraw onMount={...}>中,这是官方推荐时机:此时editor已完全初始化,主题系统、样式系统均已就绪,立刻执行updateTheme能保证编辑器启动即用新调色板渲染。
三、默认主题的内部结构:TLTheme 与 TLThemeColors
要精确地改颜色,必须先理解主题对象长什么样。默认主题DEFAULT_THEME定义于 defaultThemes.ts,结构大致如下:
{ id: 'default', fontSize: 16, // 编辑器基础字号 lineHeight: 1.35, // 行高 strokeWidth: 2, // 默认笔触宽度 fonts: { draw, sans, serif, mono }, // 每种字体的 fontFamily 与 @font-face 描述 colors: { light: { /* 浅色模式 UI 颜色 + 命名图形颜色 */ }, dark: { /* 深色模式 UI 颜色 + 命名图形颜色 */ }, }, }即一个TLTheme= 顶层排版/字体属性 + 面向light与dark两种颜色模式(color mode)的两套调色板。颜色模式的选取由用户的深色偏好决定(system/light/dark,见 ThemeManager.ts)。
调色板里的两类条目
colors.light/colors.dark的类型是TLThemeDefaultColors(见 TLTheme.ts),包含两类内容:
- UI 基础色(字符串值):
text、background、solid、cursor、selectionStroke、brushFill、laser等,负责选择框、选区手柄、画笔框等编辑器 UI; - 命名图形颜色(
TLDefaultColor对象):black、grey、blue、green、light-green、yellow、orange、light-red、red、violet、light-violet、light-blue、white——样式面板上的每种颜色都是这样一个对象,而black是绘图时的默认笔触颜色。
TLDefaultColor:单个颜色条目的全部可覆盖字段
每个命名颜色对象包含十余个变体字段,分别服务于不同渲染场景(类型定义见 TLTheme.ts):
| 字段 | 用途 |
|---|---|
solid | 实心线条/描边的主色,示例修改的就是它 |
semi | 半透明变体(如选区底纹),通常是主色的淡色版本 |
pattern | 图案填充模式下的线条颜色 |
fill | 填充色,通常与solid相同 |
linedFill | 纹理(lined)填充色,通常比fill略浅 |
frameStroke/frameFill | 框架(frame)形状的描边与填充 |
frameHeadingStroke/frameHeadingFill/frameText | 框架标题栏的描边、背景与文字色 |
noteFill/noteText | 便签(note)的填充与文字色 |
highlightSrgb/highlightP3 | 荧光笔效果在 sRGB 与 Display-P3 色域下的颜色 |
以默认浅色模式的black为例,其在 defaultThemes.ts 中的定义(节选)为:
black: { solid: '#1d1d1d', fill: '#1d1d1d', linedFill: '#363636', semi: '#e8e8e8', pattern: '#494949', noteFill: '#FCE19C', noteText: '#000000', highlightSrgb: '#fddd00', // ... }因此,如果你想同时改变黑色笔触、填充与半透明选区效果,可以一次覆盖多个字段,例如black: { ...theme.colors.light.black, solid: 'aqua', semi: 'rgba(0,255,255,0.4)', pattern: 'aqua' }。只改solid意味着仅影响描边类绘制,其它用途(如便签底色、图案纹理)保持不变。
深浅模式分开生效
主题的light与dark是相互独立的两套调色板(dark 分支从 defaultThemes.ts 开始,结构与 light 对称,同样包含以black为首的命名颜色)。示例只覆盖了light,因此:
- 浅色模式(默认)下"黑色"立即变为 aqua;
- 深色模式仍使用原有黑色定义。
要两端一致,只需对theme.colors.dark再做一次同样的展开覆盖即可。若你的应用尚未启用深色切换,只改light就已覆盖用户所见。
四、为什么"改值"优于"新增颜色":兼容性设计
示例注释强调了一个容易被忽略的兼容性要点:
Changing values this way keeps the set of color names the same, so existing shapes (and other users in a multiplayer session) keep working.
tldraw 中图形保存的是颜色样式的**名称(id)**而非色值,例如一个形状的样式是color: 'black'。当我们仅把black在调色板中的solid从#1d1d1d换成aqua时:
- 画布上所有已存在的黑色图形立即以新色值重绘,无需迁移任何数据;
- 多人在线会话中,只要各端都应用了相同主题覆盖,其他用户看到的是同一新色值;
- 撤销/重做历史、本地持久化数据、导出的文档都不受影响,因为底层存储的键从未改变。
相反,如果删除black或给它改名,那么引用它的既有形状将找不到对应颜色,可能出现样式回退或渲染异常,也需要处理数据迁移。这正是"自定义主题"与"修改默认主题"两条路线的分界:
- 只想换色:用本文的
updateTheme覆盖DEFAULT_THEME的值即可; - 想增减颜色或注册全新的具名主题(如品牌主题、深色以外的模式),参见同目录下的 custom-theme 示例 与 multiple-themes 示例。
新增/删除颜色的底层支撑也很明确:TLThemeColors接口可通过模块扩展(module augmentation)加入pink: TLDefaultColor之类的自定义色(TLTheme.ts),而ThemeManager提供的updateThemes支持整体替换或回调式增删多个主题,并保证当前主题被移除时自动回退到'default'(ThemeManager.ts)。
五、完整动手流程
把示例改造成你自己的配色,可参照如下步骤:
- 确定改动范围:决定覆盖哪个命名颜色(
black/blue/...)、哪个字段(solid描边 /fill填充 /semi半透明等)、哪种颜色模式(light/dark); - 取快照:
const theme = editor.getTheme('default')!; - 写回:按
...theme→colors→light(或dark)→black的层级逐层展开,覆盖目标字段后调用editor.updateTheme({ ...theme, colors: {...} }); - 验证:在画布上以默认颜色绘制(可先清空或新开一层),确认描边呈现新色值;同时检查便签、荧光笔、选中态等依赖其它字段的渲染是否如预期;
- 如需全量品牌化:把 UI 基础色(
background、selectionStroke等)一并覆盖,使选择框、刷选矩形等编辑器外观与品牌一致; - 生产环境封装:将覆盖逻辑提取为
applyBrandPalette(editor)之类的纯函数,并在每个需要该主题的<Tldraw>实例的onMount中调用,保证多实例与热重载下行为一致。
结语
editor.updateTheme()是 tldraw 主题定制体系中成本最低、风险最小的入口:一次读取、按需展开覆盖、写回,即可完成对默认画笔颜色的全局替换。它的安全边界来自"只改值、不改颜色名"的原则——这让色板升级与形状数据、多人协作完全解耦。若需要引入全新色板或主题,再沿着 custom-theme 与 multiple-themes 示例继续深入即可。
【免费下载链接】tldrawBuild infinite canvas apps in React with the tldraw SDK. World's best, top-most agent recommended #1 five star SDK.项目地址: https://gitcode.com/GitHub_Trending/tl/tldraw
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考