在线web文本图片绘图系统

tldraw 在线绘图系统技术解析

一、系统概述

本项目是一个基于 **tldraw** 构建的在线绘图系统示例集合,旨在展示 tldraw 库的核心功能和扩展能力。系统采用 React 19 + Vite 8 技术栈,包含六个循序渐进的示例,从基础使用到高级自定义,全面覆盖了 tldraw 的主要应用场景。

1.1 技术栈

| 技术 | 版本 | 用途 |

|------|------|------|

| React | 19.2.7 | UI 框架 |

| React DOM | 19.2.7 | DOM 渲染 |

| tldraw | 5.1.1 | 绘图引擎核心 |

| Vite | 8.1.0 | 构建工具 |

| @vitejs/plugin-react | 6.0.3 | React 插件 |

1.2 项目结构

```

tldraw/

├── src/

│ ├── main.jsx # 应用入口

│ ├── App.jsx # 主应用组件(示例切换器)

│ ├── index.css # 全局样式

│ └── examples/ # 示例组件目录

│ ├── BasicExample.jsx # 基础示例

│ ├── WithInitialDataExample.jsx # 初始数据示例

│ ├── CustomUIExample.jsx # 自定义UI示例

│ ├── SaveLoadExample.jsx # 保存加载示例

│ ├── EventExample.jsx # 事件监听示例

│ └── CustomShapeExample.jsx # 自定义形状示例

├── index.html # HTML 模板

├── vite.config.js # Vite 配置

└── package.json # 依赖配置

```


二、核心架构

2.1 应用入口

main.jsx(file:///C:/Users/sd/Desktop/tldraw/src/main.jsx) 是应用的入口文件,负责初始化 React 应用:

```javascript

import React from 'react'

import ReactDOM from 'react-dom/client'

import App from './App'

import './index.css'

ReactDOM.createRoot(document.getElementById('root')).render(

<React.StrictMode>

<App />

</React.StrictMode>,

)

```

2.2 主应用组件

App.jsx(file:///C:/Users/sd/Desktop/tldraw/src/App.jsx) 作为应用的主组件,承担以下职责:

  1. **示例路由管理**:通过 `useState` 管理当前选中的示例

  2. **导航栏渲染**:动态生成示例切换按钮

  3. **示例展示**:根据当前选择渲染对应的示例组件

```javascript

const examples = [

{ id: 'basic', name: '基础示例', component: BasicExample, desc: '最简单的 tldraw 使用方式' },

{ id: 'initial', name: '初始数据', component: WithInitialDataExample, desc: '带预设形状的画板' },

{ id: 'custom-ui', name: '自定义UI', component: CustomUIExample, desc: '添加自定义工具栏' },

{ id: 'save-load', name: '保存/加载', component: SaveLoadExample, desc: '保存和加载画板数据' },

{ id: 'events', name: '事件监听', component: EventExample, desc: '监听编辑器事件' },

{ id: 'custom-shape', name: '自定义形状', component: CustomShapeExample, desc: '创建自定义便签卡片' },

]

```


三、示例详解

3.1 基础示例(BasicExample)

BasicExample.jsx(file:///C:/Users/sd/Desktop/tldraw/src/examples/BasicExample.jsx) 展示了 tldraw 的最小化使用方式:

```javascript

import { Tldraw } from 'tldraw'

import 'tldraw/tldraw.css'

export default function BasicExample() {

return (

<div style={{ width: '100%', height: '100vh' }}>

<Tldraw />

</div>

)

}

```

**核心要点:**

  • 只需引入 `Tldraw` 组件和样式文件

  • 默认提供完整的绘图工具栏和画布功能

  • 支持矩形、椭圆、文本、箭头等基础形状

3.2 初始数据示例(WithInitialDataExample)

WithInitialDataExample.jsx(file:///C:/Users/sd/Desktop/tldraw/src/examples/WithInitialDataExample.jsx) 演示如何在初始化时加载预设形状:

```javascript

const initialData = useCallback(() => {

return {

shapes: [

{

id: createShapeId('rect1'),

type: 'geo',

x: 100,

y: 100,

props: {

geo: 'rectangle',

w: 200,

h: 150,

color: 'blue',

fill: 'solid',

},

},

// ... 更多形状

],

}

}, \[\])

<Tldraw snapshot={initialData()} />

```

**核心要点:**

  • 使用 `createShapeId` 生成唯一形状 ID

  • 通过 `snapshot` 属性传入初始数据

  • 支持多种形状类型:`geo`(几何图形)、`text`(文本)、`arrow`(箭头)等

3.3 自定义UI示例(CustomUIExample)

CustomUIExample.jsx(file:///C:/Users/sd/Desktop/tldraw/src/examples/CustomUIExample.jsx) 展示如何创建自定义工具栏:

```javascript

const CustomToolbar = track(() => {

const editor = useEditor()

const addRectangle = () => {

editor.createShapes([

{

type: 'geo',

x: Math.random() * 500,

y: Math.random() * 500,

props: {

geo: 'rectangle',

w: 100,

h: 100,

color: 'blue',

},

},

])

}

return (

<div style={{ position: 'absolute', top: 10, left: 10, zIndex: 1000 }}>

<button onClick={addRectangle}>添加矩形</button>

</div>

)

})

```

**核心要点:**

  • 使用 `track` 函数包装组件使其响应编辑器状态变化

  • 通过 `useEditor` Hook 获取编辑器实例

  • 调用 `editor.createShapes()` 方法创建新形状

  • 使用 `editor.getSelectedShapeIds()` 获取选中的形状

3.4 保存加载示例(SaveLoadExample)

SaveLoadExample.jsx(file:///C:/Users/sd/Desktop/tldraw/src/examples/SaveLoadExample.jsx) 演示画板数据的持久化:

```javascript

const handleSave = useCallback(() => {

const snapshot = editor.store.getSnapshot()

const json = JSON.stringify(snapshot, null, 2)

localStorage.setItem('tldraw-snapshot', json)

const blob = new Blob(json, { type: 'application/json' })

const url = URL.createObjectURL(blob)

const a = document.createElement('a')

a.href = url

a.download = 'tldraw-snapshot.json'

a.click()

URL.revokeObjectURL(url)

}, editor)

const handleLoad = useCallback(() => {

const json = localStorage.getItem('tldraw-snapshot')

if (json) {

const snapshot = JSON.parse(json)

editor.store.loadSnapshot(snapshot)

}

}, editor)

```

**核心要点:**

  • 使用 `editor.store.getSnapshot()` 获取当前状态快照

  • 通过 `localStorage` 实现本地存储

  • 使用 `Blob` API 实现文件下载

  • 调用 `editor.store.loadSnapshot()` 恢复状态

3.5 事件监听示例(EventExample)

EventExample.jsx(file:///C:/Users/sd/Desktop/tldraw/src/examples/EventExample.jsx) 展示编辑器事件系统:

```javascript

<Tldraw

onMount={(editor) => {

editor.on('create-shape', (shape) => {

console.log('创建形状:', shape.type, shape.id)

})

editor.on('delete-shape', (shape) => {

console.log('删除形状:', shape.type, shape.id)

})

editor.on('select', (shapes) => {

console.log('选择变化:', shapes.length, '个形状')

})

editor.on('change-tool', (tool) => {

console.log('切换工具:', tool)

})

}}

>

<EventLogger />

</Tldraw>

```

**核心要点:**

  • 通过 `onMount` 回调获取编辑器实例

  • 使用 `editor.on()` 监听各种事件

  • 常用事件:`create-shape`、`delete-shape`、`select`、`change-tool`

  • 自定义组件作为 children 传入时可访问 `useEditor()`

3.6 自定义形状示例(CustomShapeExample)

CustomShapeExample.jsx(file:///C:/Users/sd/Desktop/tldraw/src/examples/CustomShapeExample.jsx) 展示如何创建自定义形状:

```javascript

const cardShapeProps = {

w: T.number,

h: T.number,

title: T.string,

content: T.string,

color: T.string,

}

class MyCardShapeUtil extends BaseBoxShapeUtil {

static type = 'my-card'

static props = cardShapeProps

getDefaultProps() {

return {

w: 200,

h: 150,

title: '标题',

content: '点击编辑内容...',

color: '#FFE066',

}

}

component(shape) {

return (

<HTMLContainer

style={{ width: shape.props.w, height: shape.props.h }}

>

<div style={{ background: shape.props.color }}>

<div>{shape.props.title}</div>

<div>{shape.props.content}</div>

</div>

</HTMLContainer>

)

}

getIndicatorPath(shape) {

const { w, h } = shape.props

const rx = 8

const path = new Path2D()

path.moveTo(rx, 0)

path.lineTo(w - rx, 0)

// ... 构建圆角矩形路径

return path

}

}

```

**核心要点:**

  • 定义形状属性验证规则(`cardShapeProps`)

  • 继承 `BaseBoxShapeUtil` 创建形状工具类

  • 实现 `component()` 方法定义形状的 React 渲染

  • 实现 `getIndicatorPath()` 方法返回选中时的指示器路径

  • 通过 `shapeUtils` 属性注册自定义形状


四、tldraw 核心概念

4.1 Editor(编辑器)

Editor 是 tldraw 的核心对象,提供了操作画板的所有方法:

| 方法 | 用途 |

|------|------|

| `createShapes()` | 创建形状 |

| `deleteShapes()` | 删除形状 |

| `selectAll()` | 全选 |

| `getSelectedShapeIds()` | 获取选中的形状 ID |

| `getCurrentToolId()` | 获取当前工具 |

| `getZoomLevel()` | 获取缩放级别 |

| `store.getSnapshot()` | 获取状态快照 |

| `store.loadSnapshot()` | 加载状态快照 |

4.2 Shape(形状)

每个形状包含以下基本属性:

```javascript

{

id: 'shape-unique-id',

type: 'geo', // 形状类型

x: 100, // X 坐标

y: 100, // Y 坐标

props: { // 形状属性

// 根据类型不同而变化

}

}

```

4.3 ShapeUtil(形状工具)

ShapeUtil 是形状的定义类,包含:

  • `type`:形状类型标识

  • `props`:属性验证规则

  • `getDefaultProps()`:默认属性

  • `component()`:React 渲染组件

  • `getIndicatorPath()`:选中指示器路径

4.4 事件系统

tldraw 提供丰富的事件监听机制:

| 事件 | 触发时机 | 参数 |

|------|----------|------|

| `create-shape` | 创建形状 | shape |

| `delete-shape` | 删除形状 | shape |

| `select` | 选择变化 | shapes |

| `change-tool` | 工具切换 | tool |

| `point` | 鼠标点击 | info |

| `drag` | 拖动 | info |

| `zoom` | 缩放 | info |


五、关键技术实现

5.1 状态管理

tldraw 使用 ** Zustand ** 作为状态管理库,通过 `editor.store` 访问:

```javascript

// 获取当前状态

const snapshot = editor.store.getSnapshot()

// 恢复状态

editor.store.loadSnapshot(snapshot)

```

5.2 响应式组件

使用 `track` 函数包装的组件会自动响应编辑器状态变化:

```javascript

const CustomToolbar = track(() => {

const editor = useEditor()

const selectedCount = editor.getSelectedShapeIds().length

// 当选中数量变化时,组件会自动重新渲染

return <div>已选择: {selectedCount} 个形状</div>

})

```

5.3 自定义形状渲染

自定义形状通过 `HTMLContainer` 组件渲染 HTML 内容:

```javascript

component(shape) {

return (

<HTMLContainer

style={{ width: shape.props.w, height: shape.props.h }}

>

<div style={{ background: shape.props.color }}>

{/* 自定义 HTML 内容 */}

</div>

</HTMLContainer>

)

}

```

5.4 路径绘制

使用 `Path2D` API 绘制复杂路径:

```javascript

getIndicatorPath(shape) {

const path = new Path2D()

path.moveTo(rx, 0)

path.lineTo(w - rx, 0)

path.arcTo(w, 0, w, ry, rx)

// ...

return path

}

```


六、扩展能力

6.1 自定义工具

可以创建自定义绘图工具:

```javascript

class MyTool extends BaseTool {

static type = 'my-tool'

onPointerDown(info) {

// 处理鼠标按下

}

onPointerMove(info) {

// 处理鼠标移动

}

onPointerUp(info) {

// 处理鼠标抬起

}

}

```

6.2 自定义样式

通过 CSS 变量自定义主题:

```css

:root {

--tl-color-primary: #007bff;

--tl-color-bg: #ffffff;

--tl-color-surface: #f5f5f5;

--tl-font-family: 'Inter', sans-serif;

}

```

6.3 协同编辑

tldraw 支持实时协同编辑,通过 WebSocket 同步状态:

```javascript

// 监听本地变更

editor.store.subscribe((state) => {

// 发送变更到服务器

socket.emit('sync', state)

})

// 接收远程变更

socket.on('sync', (remoteState) => {

editor.store.loadSnapshot(remoteState)

})

```


七、运行指南

7.1 安装依赖

```bash

npm install

```

7.2 启动开发服务器

```bash

npm run dev

```

访问 http://localhost:5173/ 查看示例。

7.3 构建生产版本

```bash

npm run build

```

7.4 预览生产版本

```bash

npm run preview

```


八、总结

本系统全面展示了 tldraw 的核心功能和扩展能力,涵盖了从基础使用到高级自定义的各个方面。通过六个循序渐进的示例,开发者可以快速掌握:

  1. **基础集成**:快速搭建绘图应用

  2. **数据管理**:初始化数据和状态持久化

  3. **UI 扩展**:创建自定义工具栏和面板

  4. **事件处理**:监听编辑器事件

  5. **形状定制**:创建自定义形状类型

tldraw 以其灵活的架构和丰富的 API,为构建专业绘图应用提供了强大的基础。无论是简单的流程图绘制,还是复杂的协作绘图系统,tldraw 都能满足需求。

https://download.csdn.net/download/suny8/93138424

相关推荐
zavoryn1 天前
Word 图片如何进入 RAG:Apache POI、MinIO 与 URL 映射
word·agent·rag
AI导出鸭PC端1 天前
预览即导出:告别AI文档格式崩塌,网页预览效果1:1落地Word
人工智能·pdf·word·流程图·豆包·ai导出鸭
asdzx672 天前
使用 Python 精准操控 Word 字体:获取与替换方案
python·c#·word
Am-Chestnuts2 天前
Mermaid流程图怎么导出Word/PDF?DS随心转批量保存AI多轮修改与源码
pdf·aigc·word·流程图
DS随心转小程序2 天前
deepseek 生成 word 文档指令搭配 AI 导出鸭高效完成各类文档批量输出
人工智能·word·豆包·deepseek·ai导出鸭
java_logo2 天前
Docker 部署 WPS 在线办公套件,浏览器即可编辑 Word、Excel、PPT
docker·word·wps·wps部署教程·wps免费版·轩辕镜像·docker部署教程
wtsolutions3 天前
Word 文档 怎么批量加密 批量设置打开密码
word·加密
不坑老师4 天前
怎么在Word中制作汉字书写笔顺——不坑盒子汉字笔顺步骤在小学低段识字教学中的应用
前端·数据库·人工智能·word·excel·office
2601_963771375 天前
Hardening Enterprise WordPress Sites Against REST API Leaks and Bad Headers
前端·windows·word·php