2025-11-21-canvas项目杂记
最后更新时间:
页面浏览: 加载中...
分析
要做的就是一个类 Canva / Figma 的在线图形绘板,完整需求优先级和覆盖范围如下:
| 优先级 | 功能模块 | 具体需求 | 是否必须 | 难度 |
|---|---|---|---|---|
| P0 | 基础渲染 | 矩形、圆形、三角形任意填充色、边框色、边框宽度、圆角、透明度 | Yes | ★☆ |
| P0 | 图片支持 | 上传 png/jpg/webp,任意缩放、圆角、模糊、灰度、亮度调节、裁剪掩模 | Yes | ★★ |
| P0 | 富文本 | 字体、字号、颜色、加粗、斜体、下划线、删除线、文本背景色、文字对齐、行距、局部样式支持 | Yes | ★★★★ |
| P0 | 基本交互 | 单选、多选(框选 + Shift)、拖拽、删除、复制粘贴、缩放把手(8 个方向)、旋转把手 | Yes | ★★ |
| P0 | 无限画布 + 缩放平移 | Ctrl+滚轮缩放、空格拖拽平移、无限滚动 | Yes | ★☆ |
| P0 | 数据持久化 | 自动 localStorage 保存、打开页面自动恢复 | Yes | ★☆ |
| P1 | 高级交互 | 组合(Group)、解散组合、图层排序、辅助对齐线、吸附、旋转任意角度 | Yes | ★★★ |
| P1 | 工具栏 & 属性面板 | 顶部工具栏(切换文本/形状/图片模式)、右侧属性面板实时编辑属性 | Yes | ★★ |
| P1 | 历史记录 | Undo / Redo(支持跨会话) | Yes | ★★ |
| P1 | 性能要求 | 100 个复杂元素(图片+富文本)打开 < 3s,拖拽 60fps 不闪烁 | Yes | ★★★★ |
| P2 | 未来可扩展 | 实时协同编辑、离线编辑、模板库、导出 PNG/SVG/PDF、激光笔、箭头、自由画笔等 | No | ★★★★ |
架构方案
渲染层:PixiJS (WebGL) 处理高性能图形渲染 + HTML DOM 处理文本编辑/输入框。状态管理:Zustand / Pinia (管理庞大的 JSON 画布数据)。逻辑层:自定义 Class 结构(如 Shape, Tool, History)实现面向对象编程。
二、项目设计要素
| 设计维度 | 推荐技术方案 | |
|---|---|---|
| 1. 渲染引擎 | PixiJS v8(WebGL) + HTMLText | WebGL 抗锯齿完美 + 高分屏不模糊;HTMLText 是目前唯一能轻松实现富文本局部样式的方案 |
| 2. 状态管理 | Zustand(或 Jotai + signals) | 轻量、响应式、支持中间件(持久化、历史栈) |
| 3. 元素对象缓存 | Map |
彻底解决闪烁、拖拽中断、光标丢失的根本方案 |
| 4. 历史栈 | Command Pattern + structuredClone 快照(每操作记录 before/after) | 简单可靠,支持跨页面 Undo |
| 5. 选中/变换系统 | 单独的 SelectionManager + TransformHandles(旋转、缩放把手层也缓存) | tldraw/Figma 标配 |
| 6. 辅助对齐线 | 拖拽时实时遍历所有元素 bounds,差值 < 5px 就吸附并画蓝线 | 提升专业感 |
| 7. 组合(Group) | 元素加 groupId 字段;选中时绘制大虚线框;拖拽/缩放/旋转时整体应用矩阵变换 | 必须有,属于 P1 核心 |
| 8. 数据持久化 | Zustand middleware persist + localForage(IndexedDB) | 防止 localStorage 炸掉 |
| 9. 图片处理 | Sprite + Graphics mask(圆角)+ BlurFilter + ColorMatrixFilter | PixiJS 原生支持 |
| 10. 架构分层 | - store(纯数据) - rendering(Pixi 元素缓存 & 更新) - interaction(拖拽、选中逻辑) - ui(React 面板) |
installed pixi.js@8.14.3installed zustand@5.0.8installed nanoid@5.1.6
数据驱动视图”(Data-Driven View) 模式,采用了 React (UI) + Zustand (数据) + PixiJS (渲染) 的三层分离架构
这种架构的核心理念是:PixiJS 实例不保存“业务状态”,它只是 Zustand 数据的“投影”

其中,
React 只负责 UI 和事件入口Zustand 是唯一的真实数据源(纯 JSON,可持久化、可协同)PixiJS 层只做“渲染 + 交互计算”,所有对象永久缓存(Map),绝不每帧重建所有变换(拖拽、缩放、旋转、组合)都在 Pixi 层完成,最后再同步回 Zustand(单向数据流)
三层架构详解
第一层:数据层 (The Source of Truth) - canvasStore.ts
这是整个应用的大脑。
职责:只存储纯 JSON 数据(Serializable),不包含任何 UI 实例或 Pixi 对象。存储内容:elements: 一个 Map 对象(Record),存储所有矩形、圆形的坐标、颜色等。 selectedIds: 当前选中的 ID 列表。tool: 当前使用的工具。
特点:单一数据源:画布上显示什么,完全由这里的数据决定。无副作用:这里的 Action 只修改数据,不直接操作 DOM 或 Canvas。
第二层:适配层 (The Bridge) - StageManager.ts
这是连接 React/Zustand 和 PixiJS 的胶水层,也是架构中最复杂的部分。
职责:将“声明式”的数据(Zustand)转换为“命令式”的 Pixi 调用。核心机制 - 增量更新 (Diffing):它维护了一个 spriteMap (Map)。 订阅 (Subscribe):它监听 Store 的变化。同步 (Sync/Render Loop):Create: Store 有 ID,Map 里没有 -> new PIXI.Graphics()。Update: Store 有,Map 里也有 -> 更新 x, y, width, color。Delete: Store 没有,Map 里有 -> destroy()。
事件转换:它监听 Pixi 的 pointerdown/move/up 事件,将屏幕坐标转换为逻辑坐标,然后调用 Store 的 Action。
第三层:视图层 (The Container) - Canvas.tsx
这是 React 组件层。
职责:提供 div 容器供 Pixi 挂载。渲染 HTML UI(工具栏、属性面板)。生命周期管理:组件 Mount 时初始化 StageManager,Unmount 时销毁。
—-
3. 关键数据流转 (Data Flow)
让我们以 “拖拽矩形移动” 为例,看数据如何在架构中流转:
Input (输入):
用户在画布上按住矩形并移动鼠标。StageManager 的 onPointerMove 被触发。
Logic (逻辑处理):
StageManager 计算鼠标的偏移量 (dx, dy)。它不直接修改 Pixi 图形的 graphics.x (这是关键!)。它调用 store.updateElement(id, { x: newX, y: newY })。
State Update (状态更新):
Zustand Store 更新内部的 JSON 数据。Zustand 触发订阅回调 (subscribe)。
Render Sync (渲染同步):
StageManager 的 render 方法被调用。它从 spriteMap 找到对应的 Pixi 实例。执行 graphic.position.set(newX, newY)。PixiJS 在下一个 requestAnimationFrame 自动重绘 Canvas。
—-
4. 为什么选择这种架构?
优点:
解耦 (Decoupling):渲染引擎可以随时替换(比如换成 Konva 或原生 Canvas),只需要重写 StageManager,数据层和 UI 层不需要动。
协同编辑 (Collaboration) 友好:
如果要实现多入协同,只需要监听 WebSocket 消息,然后更新 Zustand Store。StageManager 会自动把队友的操作画出来,无需写额外的同步绘图逻辑。撤销/重做 (Undo/Redo) 容易:因为所有状态都在 Store 里,只需要保存/恢复 Store 的快照(或 Patch)即可。序列化/反序列化:保存项目只需 JSON.stringify(store.elements)。
潜在挑战(及优化方案):
性能瓶颈:
问题:高频触发 Store Update -> Diff 循环可能在元素极多时(>2000 个)产生开销。优化:对于拖拽这种 60FPS 的操作,可以引入“临时层” (Transient State)。即拖拽时直接修改 Pixi 对象,鼠标松开时再同步到 Store。复杂性:相比直接用 Canvas API 画图,这种架构代码量更大,需要维护 ID 映射和 Diff 逻辑。
feat(canvas): 重构画布实现,应项目要求,删除了基于tldraw的实现转而选择PixiJS 库重构以进行渲染操作,基于Zustand 进行状态管理。新增的文件中canvasStore.ts主要负责维护整个画布项目的全局可序列化状态,是渲染画布系统中唯一数据来源。Pixi_stageManager.ts 负责将声明式数据(Zustand)实时、高性能地映射为命令式渲染实例(PixiJS),并处理所有用户交互的计算与反馈。canvas下的index.ts最轻量的一层,仅负责生命周期管理与组件组装。三层数据驱动架构详情可见文档