)
tldraw 实战使用 ShapeUtil.configure 关闭文字标签的文本描边showTextOutline【免费下载链接】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/tldrawtldraw 默认会为画布上的文字、箭头标签与几何图形标签绘制一圈与背景色相同的“光晕halo”以保障文字在与其他形状重叠时仍清晰可读。本篇文章基于仓库中的官方示例 custom-text-outline 示例完整讲解showTextOutline选项的作用、它由哪些 Shape Util 承载、如何借助ShapeUtil.configure一次性为多个形状工具关闭该效果并深入源码解释描边的真实绘制机制、性能取舍与低细节LOD自动降级逻辑。读完你既能照抄一段可运行的配置代码也能理解 tldraw 文字渲染背后的实现细节从而在自己的 React 画布应用中按需定制文本样式。一、什么是文本描边Text Outline当多个形状相互重叠时如果文本与背后的形状颜色接近会很难分辨文字边界。tldraw 的默认解法是在文字周围绘制一圈与画布背景色一致的“光晕”。它在视觉上相当于给每个文字描了一层背景色轮廓让字与后面的图形“隔开”。这一效果不是矢量描边而是基于CSS 多重text-shadow实现的。从 editor.css 的变量定义可以看到它的真实构成--tl-text-outline-a: calc(min(0.5, 1 / var(--tl-zoom)) * 2px); --tl-text-outline-b: calc(min(0.5, 1 / var(--tl-zoom)) * -2px); --tl-text-outline-reference: 0 var(--tl-text-outline-b) 0 var(--tl-color-background), 0 var(--tl-text-outline-a) 0 var(--tl-color-background), var(--tl-text-outline-b) var(--tl-text-outline-b) 0 var(--tl-color-background), var(--tl-text-outline-a) var(--tl-text-outline-b) 0 var(--tl-color-background), var(--tl-text-outline-a) var(--tl-text-outline-a) 0 var(--tl-color-background), var(--tl-text-outline-b) var(--tl-text-outline-a) 0 var(--tl-color-background); --tl-text-outline: var(--tl-text-outline-reference);这段代码使用 6 个向不同方向偏移、0 模糊半径的背景色阴影从 8 个方位拼出文字外围的 2px “描边”而偏移量--tl-text-outline-a/-b又乘以1 / var(--tl-zoom)也就是说阴影厚度会随画布缩放而收缩避免缩小视图时文字边缘被描边糊成一团。只要把--tl-text-outline置为none即可从全局移除描边。二、谁负责渲染文本showTextOutline出现在哪里tldraw 中所有文本标签都由统一的标签组件渲染真正绘制描边的是这两个共享组件RichTextLabel.tsx支持富文本的就地编辑标签。当showTextOutline为true时根元素带tl-text__outline类否则带tl-text__no-outline类PlainTextLabel.tsx同样按showTextOutline切换tl-text__outline/tl-text__no-outlineRichTextSVG位于同一文件导出 SVG 时直接以内联样式写textShadow: showTextOutline ? var(--tl-text-outline) : none。而showTextOutline布尔值最终来自各个形状工具的options。仓库示例明确说明每一个会渲染文本标签的 Shape Util 都自带一个showTextOutline选项包括Shape Util渲染的文本默认值源码位置TextShapeUtil纯文本形状正文文字trueTextShapeUtil.tsxArrowShapeUtil箭头箭头中段/端点的标签trueArrowShapeUtil.tsxGeoShapeUtil矩形、椭圆、对话气泡等几何图形图形内部标签trueGeoShapeUtil.tsx以文本形状为例TextShapeUtil.tsx 中的options是这样声明的override options: TextShapeOptions { extraArrowHorizontalPadding: 10, // 文字被箭头绑定时额外增加的水平几何宽度 showTextOutline: true, // 是否绘制文本描边 getDefaultDisplayValues(_editor, shape, theme, colorMode) { /* ... */ }, getCustomDisplayValues() { /* ... */ }, }值得注意的一个特殊实现是便签Note虽然笔记文字也通过标签组件渲染但 NoteShapeUtil.tsx 的渲染代码中写死了showTextOutline{false}。也就是说贴纸/便签类形状从设计上就不走描边方案它本身是黄底卡片、靠底色区分你不能也不需要通过configure去改变它。三、核心 API静态方法ShapeUtil.configure示例中关闭描边的关键写法是TextShapeUtil.configure({ showTextOutline: false })。configure是抽象基类ShapeUtil上的一个静态方法因此所有继承它的形状工具都可用。它的实现位于 packages/editor/src/lib/editor/shapes/ShapeUtil.ts#L109-L120static configureT extends TLShapeUtilConstructorany, any( this: T, options: T extends new (...args: any[]) { options: infer Options } ? PartialOptions : never ): T { return class extends this { options { ...this.options, ...options } } }这个方法做的事情非常轻量而精妙类型安全options参数的类型被约束为该 Util 实例上options字段的Partial所以传错键名会在编译期直接报错返回新子类而非原地修改configure返回一个继承了当前类、仅覆盖options的新类旧的 Shape Util 完全不受影响浅合并选项新子类的options { ...this.options, ...options }因此你只需给出要改的字段如showTextOutline其余选项保持默认。理解“返回新类”这一点很重要——它解释了示例里为什么用“configure 后的数组”传给Tldraw shapeUtils而不是在别处做全局开关。四、完整可运行的示例解读仓库示例源码位于 CustomTextOutlineExample.tsx核心逻辑如下import { ArrowShapeUtil, GeoShapeUtil, TextShapeUtil, Tldraw, toRichText } from tldraw import tldraw/tldraw.css // [1] 分别配置三个渲染文本的 Shape Util const shapeUtils [ ArrowShapeUtil.configure({ showTextOutline: false }), TextShapeUtil.configure({ showTextOutline: false }), GeoShapeUtil.configure({ showTextOutline: false }), ] export default function CustomTextOutlineExample() { return ( div classNametldraw__editor Tldraw shapeUtils{shapeUtils} persistenceKeycustom-text-outline-example onMount{(editor) { if (editor.getCurrentPageShapeIds().size 0) return // [2] 叠放三行文字与一根带标签的箭头 const message toRichText(very good whiteboard) editor.createShapes([ { type: text, x: 100, y: 100, props: { richText: message } }, { type: text, x: 110, y: 110, props: { richText: message } }, { type: text, x: 120, y: 120, props: { richText: message } }, { type: arrow, x: 0, y: 0, props: { richText: toRichText(hello world), start: { x: 0, y: 0 }, end: { x: 200, y: 200 }, }, }, ]) }} / /div ) }关键点拆解第 [1] 步逐类配置并替换默认 Util。tldraw 的Tldraw组件默认使用内置 shapeUtils当你在shapeUtils属性里传入“configure 后的新类”时它会整体替换同类型的默认 Util。因此你需要在文本、箭头、几何图形三处分别配置——三个 Util 各自维护自己的options不存在一个全局开关。第 [2] 步构造重叠场景用于对比。onMount里先通过editor.getCurrentPageShapeIds().size 0判空避免刷新后重复创建随后用toRichText(...)把普通字符串转成 tldraw 的富文本对象richText属性要求再以微小的坐标偏移(100,100)、(110,110)、(120,120)叠放三行同样文字并加了一根从(0,0)指向(200,200)、带 “hello world” 标签的箭头。persistenceKey只是让该示例在 localStorage 中拥有独立的持久化槽位防止与其他演示互相污染状态与本功能无关。打开该示例时若showTextOutline保持默认true三行重叠的文字各自会有背景色光晕与底层文字“隔离”而在本示例中描边被整体关闭重叠处文字会直接穿插交叠在一起视觉上更“融为一体”。官方示例注释建议你把某一个 Util 的showTextOutline改回true对比观察你会发现只有该类型的标签恢复光晕其余仍保持无描边状态这正是逐 Util 配置语义的直接体现。五、为什么关闭描边视觉风格与性能关掉描边最直接的理由是视觉风格无描边让重叠文本显得更“扁平”、更像手写叠涂适合强调层次感的场景。而另一个同样重要的理由是性能。描边由多重text-shadow实现而text-shadow会在某些浏览器上触发昂贵的重绘与合成开销。tldraw 对此做了两层内置防御你可以结合源码理解它们与showTextOutline的关系Safari 直接强制关闭。在 DefaultCanvas.tsx 的setTextOutline副作用里一旦检测到tlenv.isSafari就会把根容器的--tl-text-outline设成none并标记canUpdateTextOutline false只检查一次理由正是源码注释写的 “We dont allow text outlines on safari for performance reasons”。所以即使你的showTextOutline保持默认开启在 Safari 上也不会渲染描边——README 中 “tldraw already skips it on Safari” 指的就是这条逻辑。极小缩放级别下自动降级LOD。同一副作用中还读取了editor.options.textShadowLod默认值0.35定义在 options.ts当editor.getEfficientZoomLevel()低于该阈值时容器也会把--tl-text-outline置为none仅在放大回阈值以上后恢复。也就是说在用户把画布缩小到很远处、描边既看不清又白白消耗 GPU 时tldraw 会自动“甩掉”这层阴影。对比而言通过configure关闭showTextOutline是更彻底的方案它不只是干掉 CSS 阴影而是让标签组件直接渲染为tl-text__no-outline连阴影路径都不再走参见 RichTextLabel.tsx 与 SVG 导出时的textShadow: none分支。如果你在意的是跨浏览器、不同缩放下的渲染开销这个方法比依赖平台自动降级更可控。六、把示例迁移进自己的应用把这段能力用到自己的 tldraw 应用中只需三步示例目录apps/examples/src/examples/configuration/下同类配置示例如 configure-shape-util、custom-options遵循完全相同的模式确定要影响的形状类型。text纯文本、arrow箭头标签、geo几何图形标签是最常出现文本重叠的三类若你的场景中还有其它自带文字的自定义 Util只要它通过RichTextLabel/PlainTextLabel渲染且暴露了showTextOutlineoption也可一并配置。生成配置后的 Util 数组并原样传给Tldraw shapeUtils{...}import { TextShapeUtil, Tldraw } from tldraw import tldraw/tldraw.css // 仅关闭纯文本形状的描边 const shapeUtils [TextShapeUtil.configure({ showTextOutline: false })] export default function App() { return div classNametldraw__editorTldraw shapeUtils{shapeUtils} //div }验证与回退。渲染重叠形状后确认文字不再有背景色光晕如需某个类型恢复默认从shapeUtils数组中移除对应项不传即用内置默认 Util或在配置里把该项改回true。由于configure返回新类而不污染原类你在同一次渲染中混用“开描边”和“关描边”的不同版本也不会互相干扰。如果你希望走“全局关闭”而非“逐类配置”也可以自行把--tl-text-outline覆盖为none编辑应用样式层面但这不属于 Shape Util options 体系示例采用的configure路线才是面向 SDK 类型系统、可随迁移与类型检查持续生效的推荐方式。七、小结围绕“自定义文本描边”本文从仓库示例出发覆盖了完整知识链先说明描边本质是editor.css中 6 组背景色text-shadow拼出的缩放自适应光晕再指出它由TextShapeUtil、ArrowShapeUtil、GeoShapeUtil各自通过options.showTextOutline默认true控制而NoteShapeUtil写死为false随后演示了ShapeUtil.configure返回“浅合并 options 的子类”的机制并用一段可直接运行的示例代码展示逐类替换shapeUtils的用法最后结合 DefaultCanvas.tsx 与textShadowLod说明了 tldraw 在 Safari 与低缩放级别下的自动降级帮助你判断何时值得主动关闭描边以换取更低的渲染开销。核心参考资料官方示例 custom-text-outline/README.md、实现文件 CustomTextOutlineExample.tsx、配置 API ShapeUtil.configure 与描边实现 RichTextLabel.tsx、PlainTextLabel.tsx、editor.css。想自行试验时在示例应用中打开custom-text-outline入口把任意一个 Util 的showTextOutline拨回true即可直观对比光晕有无的效果差异。【免费下载链接】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),仅供参考