tldraw 修改默认样式指南:用 StyleProp.setDefaultValue 控制新形状的默认 size、color、dash 与 fill
【免费下载链接】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
tldraw中每个样式属性(style prop)都带有内置默认值:新建形状默认使用m尺寸、black颜色、draw线条样式等。本文以仓库中的官方示例 changing-default-style 为骨架,讲解如何通过setDefaultValue在运行时替换这些默认值,并结合@tldraw/tlschema中 StyleProp 的实现 深入说明其原理、可用取值与调用时机。读完本文后,你将能在自己的 tldraw 应用中随心定制“新图形出厂样式”,也能把同一套方法迁移到自定义样式属性上。
从一个官方示例开始:让新形状默认变小
官方示例的核心诉求很直接:把内置size样式的默认值从m改为s,使之后新建的每个形状都默认是小号。
完整的示例组件位于 ChangingDefaultStyleExample.tsx,其注册说明见同目录下的 README.md。核心代码只有几行:
import { DefaultSizeStyle, Tldraw } from 'tldraw' import 'tldraw/tldraw.css' // Call this at module level, before any editor is created, so every editor // (and every new shape) picks up the new default. DefaultSizeStyle.setDefaultValue('s') export default function ChangingDefaultStyleExample() { return ( <div className="tldraw__editor"> <Tldraw persistenceKey="changing-default-style-example" /> </div> ) }逐行拆解:
DefaultSizeStyle是 tldraw 导出的一组内置样式属性常量之一,定义在 TLSizeStyle.ts;setDefaultValue('s')把该样式属性的默认值改写为's';- 注释中的“module level”(模块顶层)是成败关键:
setDefaultValue必须在任何Tldraw/Editor实例创建之前执行,改动才会对所有编辑器与之后创建的所有新形状生效; <Tldraw persistenceKey="changing-default-style-example" />用于示例数据的本地持久化隔离;外层<div className="tldraw__editor">是各示例共用的容器类名,用来占据视口并把画布铺满(tldraw/tldraw.css已随包导出并被引入)。
在仓库中启动该示例的方式与其它apps/examples示例一致:在apps/examples目录安装依赖后,按 package.json 中的开发脚本(vite dev server)启动,再打开本示例对应的路由即可交互验证——新建一个形状,其描边、文字等尺寸相关表现均为小号。
setDefaultValue 的底层实现
setDefaultValue并不是什么魔法,它本质上是StyleProp类上的一个普通实例方法。看 StyleProp.ts 的实现:
/** @internal */ protected constructor( readonly id: string, public defaultValue: Type, readonly type: T.Validatable<Type> ) {} setDefaultValue(value: Type) { this.defaultValue = value }可见:
defaultValue在构造时作为公开可读写字段(public defaultValue)保存,setDefaultValue只是对它重新赋值,API 语义更清晰、便于阅读与搜索;- 每个
StyleProp还带一个唯一的id与一个type校验器,前者用于区分不同属性,后者在形状数据落库、加载时用于校验取值。
EnumStyleProp 与 defineEnum
DefaultSizeStyle、DefaultColorStyle这类“有固定枚举取值”的属性,实际由StyleProp.defineEnum创建为EnumStyleProp子类实例(见 StyleProp.ts 中 defineEnum 与 EnumStyleProp):
static defineEnum<const Values extends readonly unknown[]>( uniqueId: string, options: { defaultValue: Values[number]; values: Values } ) { const { defaultValue, values } = options return new EnumStyleProp<Values[number]>(uniqueId, defaultValue, values) }EnumStyleProp在父类基础上额外维护了一个只读的values数组,并用T.literalEnum(...values)构造取值校验器。因此DefaultSizeStyle既知道“当前默认值是什么”,也知道“合法取值有哪些”。顺带一提,枚举子类还提供了运行时扩展能力addValues/removeValues(StyleProp.ts#L130-L157),可用于向内置颜色等集合追加/移除自定义值,但需要注意同步修改相关 TypeScript 类型。
内置默认样式速查表
了解内置样式属性的默认值与合法取值,是决定“改成什么”的前提。下表信息均来自packages/tlschema/src/styles/下的真实定义:
| 样式属性 | 属性 id | 内置默认值 | 可选值 | 源码位置 |
|---|---|---|---|---|
DefaultSizeStyle | tldraw:size | 'm' | s、m、l、xl | TLSizeStyle.ts |
DefaultColorStyle | tldraw:color | 'black' | 见下方说明 | TLColorStyle.ts |
DefaultLabelColorStyle | tldraw:labelColor | 'black' | 同颜色集 | TLColorStyle.ts |
DefaultDashStyle | tldraw:dash | 'draw' | draw、solid、dashed、dotted、none | TLDashStyle.ts |
DefaultFillStyle | tldraw:fill | 'none' | none、semi、solid、pattern、fill、lined-fill | TLFillStyle.ts |
几点补充说明,避免误用:
- 颜色取值比较特殊。
TLColorStyle.ts中用于初始化的名称集合包含black、grey、light-violet、violet、blue、light-blue、yellow、orange、green、light-green、light-red、red、white等,但注释明确指出颜色命名空间的真正事实来源已经迁移到TLTheme(TLTheme 相关代码及registerColorsFromThemes会动态同步注册的色名)。也就是说,可用色名会随主题定义而变化,修改颜色默认值前建议以当前主题导出的色名为准。 size会同时影响描边粗细、文字字号等成比例元素。tldraw 在 default-shape-constants.ts 中用STROKE_SIZES、FONT_SIZES等常量把s/m/l/xl映射为具体像素数值,这也是改动 size 默认值后“肉眼可见变小/变大”的落点。draw是 tldraw 的手绘风格线条(类似手写草图质感),solid才是常规实线;fill的semi表示半透明填充,pattern/lined-fill则是基于当前颜色的纹理填充。
实战扩展:同时改写多个默认样式
setDefaultValue是普通实例方法,你可以对任意多个内置样式属性依次调用,组合出“你想要的新手形状出厂外观”。例如希望新形状默认为小号、红色、实线、实心填充:
import { DefaultColorStyle, DefaultDashStyle, DefaultFillStyle, DefaultSizeStyle, Tldraw, } from 'tldraw' import 'tldraw/tldraw.css' // 全部在模块顶层、创建编辑器之前执行 DefaultSizeStyle.setDefaultValue('s') // 默认尺寸:小 DefaultColorStyle.setDefaultValue('red') // 默认颜色:红 DefaultDashStyle.setDefaultValue('solid') // 默认线条:实线 DefaultFillStyle.setDefaultValue('solid') // 默认填充:实心 export default function CustomDefaultStylesExample() { return ( <div className="tldraw__editor"> <Tldraw /> </div> ) }实际使用中的经验要点:
- 取值必须属于该 prop 的合法枚举。
setDefaultValue的当前实现只是赋值,并不会在调用瞬间对取值做合法性校验(校验发生在数据读写时,由T.literalEnum构造的type完成)。传一个不在values里的字符串,不会立即报错,却可能在后续数据校验阶段暴露问题,因此务必对照上表取值。 - 改写是全局生效的。这些内置 prop 是模块级单例常量,
setDefaultValue改变的是进程内共享状态。如果你在不同的入口/路由中希望使用不同默认值,应当只在“创建编辑器之前唯一执行一次”的位置(例如应用入口文件)调用,避免顺序依赖带来的不确定性。 - 自定义形状同样受益。若你的自定义 shape 在 props 里复用了
DefaultSizeStyle、DefaultColorStyle等内置 prop(这正是 tldraw 官方推荐做法,见 StyleProp.ts 的类文档),那么对内置 prop 默认值的改写会一并影响这些自定义形状,无需逐个维护。
调用时机:为什么必须早于编辑器创建
该示例 README 特别强调setDefaultValue要在“任何编辑器创建之前”于模块顶层调用,其原因可以结合StyleProp的类文档(StyleProp.ts#L4-L23)理解:
同一个值可以被同时设置到大量形状上;最近一次使用的值会被自动保存,并应用到之后新建的形状。
tldraw 的样式机制本质上是一个“最近使用(last used)”追踪器:你在工具栏选择颜色、尺寸后,后续新建的形状都会沿用该值;而用户尚未做任何选择时,形状会回退到该样式属性此刻的defaultValue。setDefaultValue改写的正是这个兜底默认值。
因此,把调用放在模块顶层、早于Tldraw挂载,可以保证:
- 编辑器内部状态初始化时读到的就是新默认值;
- 新建形状、粘贴外部内容等路径在“无最近使用记录”的情况下全部落到新默认值上,行为处处一致。
如果在编辑器已经创建之后再调用,此时编辑器可能已经用旧默认值初始化了样式状态,或已有形状记录了各自显式取值,改写效果就会大打折扣、难以保证“处处生效”。
与另一种方案的关系:自定义 StyleProp 时直接给 defaultValue
除了运行时改写内置 prop,更“源头”的做法是在用StyleProp.define/StyleProp.defineEnum自定义样式属性时就指定默认值。两种方式互补:
import { T } from '@tldraw/validate' import { StyleProp } from '@tldraw/tlschema' // 数值型自定义样式:定义时直接确定默认值 const MyLineWidthProp = StyleProp.define('myApp:lineWidth', { defaultValue: 1, type: T.number, }) // 枚举型自定义样式 const MySizeProp = StyleProp.defineEnum('myApp:size', { defaultValue: 'medium', values: ['small', 'medium', 'large'], })二者都作用于StyleProp.defaultValue这一字段:
- 定义时设置适合“这个样式属性天然就该用什么默认值”的场景,属于声明式、确定性的配置;
setDefaultValue运行时改写适合对内置 prop(你无法改变其定义)或共享 prop 做全局定制,比如本文示例那样把整个应用的默认尺寸改成s。
若你的需求只是某个编辑器实例局部生效,更贴合 tldraw 架构的做法是自定义一个带默认值的StyleProp,而不是改写全局共享的内置 prop;若需求是“整个应用的新手形状一律小号”,那么示例中的setDefaultValue就是最直接、最小侵入的方案。
小结
setDefaultValue为 tldraw 的样式体系提供了一个轻量的全局定制入口:一行DefaultSizeStyle.setDefaultValue('s')即可让之后创建的每个形状默认变小。它的背后是StyleProp中简单直白的字段赋值(StyleProp.ts#L90-L92),而“何时生效”则依赖 tldraw 的“最近使用值优先、默认值兜底”样式机制。把握住三点即可用好它:取值须在合法枚举内、调用须在编辑器创建前的模块顶层、内置 prop 的改动会影响所有复用它们的形状。如需继续深入,可进一步阅读仓库中的 StyleProp 完整实现、内置样式的四个定义文件(size / color / dash / fill),以及示例编写规范,了解如何把这类“默认样式定制”沉淀为可在画廊中直接运行交互的官方示例。
【免费下载链接】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),仅供参考