Skip to content

第2章:基础概念与技术选型


2.1 什么是无限画布

无限画布(Infinite Canvas)是一种可以无限延伸、自由缩放的 2D 图形编辑界面。

核心特征

  • 画布范围理论上无限大
  • 用户通过「视口」查看画布的一部分
  • 支持平滑缩放(0.02x ~ 10x)
  • 支持自由平移

典型产品:Figma、Miro、Canva、稿定设计


2.2 传统 DOM 渲染的瓶颈

假设用户在画布上放置 1000 个元素:

传统 DOM 方案:
1. 创建 1000 个 DOM 节点
2. 每次缩放,1000 个节点都要重新计算样式
3. 每次平移,1000 个节点都要重绘
4. 浏览器的渲染管线被严重阻塞

性能数据对比

元素数量DOM 渲染 FPSWebGL 渲染 FPS
1006060
5004560
10002060
5000558
10000卡死55

DOM 的本质问题:每个元素都是独立的渲染单元,无法批量处理


2.3 为什么选择 WebGL

WebGL 是浏览器中直接操作 GPU 的接口。

核心优势

  1. 批量渲染:成百上千个元素可以在一次 GPU 调用中完成
  2. 硬件加速:矩阵变换由 GPU 并行计算,而非 CPU 串行
  3. 内存效率:纹理可在 GPU 显存中复用

一句话总结:DOM 是「逐个处理」,WebGL 是「批量处理」。


2.4 渲染引擎选型:Piso(PixiJS 定制版)

项目基于 Piso —— 一个基于 PixiJS 深度定制的 2D WebGL 渲染引擎。

为什么不直接用 PixiJS?

PixiJS 是游戏引擎,而设计编辑器有特殊需求:

需求PixiJSPiso
高精度矢量✅ SkiaCanvas 支持
文字排版基础专业级 TypeTool
Mipmap 控制自动可动态切换
多渲染器单一Canvas/WebGL/Skia

Piso 模块结构

@piso/core          ← 核心:Texture, Matrix, Point, Rectangle
@piso/display       ← 显示:Container, DisplayObject
@piso/graphics      ← 图形:Graphics(矢量绘制)
@piso/sprite        ← 精灵:Sprite(位图渲染)
@piso/app           ← 应用:Application, Ticker
@piso/math          ← 数学:Matrix, Polygon, 几何计算
@piso/sk-renderer   ← Skia 渲染器(高精度)

2.5 核心架构:三层结构

无限画布渲染引擎采用三层架构:

┌─────────────────────────────────────────────┐
│  Surface(画布表面)                          │
│  ─────────────────────────────────────────  │
│  职责:统筹全局,协调各组件                    │
└─────────────────────────────────────────────┘

          ├─────────────┬─────────────┬─────────────┐
          ↓             ↓             ↓             ↓
    ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐
    │ Viewport │  │ VmEngine │  │ Processor│  │ Plugin   │
    │  视口    │  │ 视图引擎  │  │  处理器  │  │  插件    │
    └──────────┘  └──────────┘  └──────────┘  └──────────┘

各层职责

Surface

  • 管理渲染循环(Ticker,16ms/帧)
  • 创建和销毁各子系统
  • 提供对外 API

Viewport

  • 管理相机位置(position)和缩放(zoom)
  • 坐标转换(屏幕坐标 ⇄ 世界坐标)
  • 视口边界限制

VmEngine

  • 数据模型 → 渲染对象映射
  • 对象池管理
  • 增量更新

Processor

  • 处理 Action(add/remove/update/move)
  • 驱动 VmEngine 更新

Plugin

  • 扩展能力(参考线、标尺、轮廓等)

2.6 数据流:从数据到像素

用户操作如何最终显示在屏幕上?

1. 用户拖动元素

2. 编辑器框架更新数据模型(Model)

3. 生成 Action:{ type: 'update_elements', ... }

4. Surface.commit(action)

5. Processor 处理 Action

6. VmEngine 更新对应的 Vm

7. Vm.setState() 触发 render()

8. 下一帧 Ticker 执行,GPU 绘制

9. 像素显示在屏幕上

关键点:数据变更 → Action → Vm 更新 → 渲染。这是「响应式」设计。


2.7 核心类关系图

                     Surface

        ┌───────────────┼───────────────┐
        │               │               │
    Viewport        VmEngine       EventBoundary
        │               │               │
        │          ┌────┴────┐          │
        │          │         │          │
        ↓        PageVm    ElementVm    │
    Position       │         │          │
    Zoom           └────┬────┘          │
                        │               │
                        ↓               │
                   PixiJS Stage ←───────┘


                   WebGL Context


                      GPU

2.8 关键代码入口

Surface 初始化

typescript
// infinite-renderer/src/surfaces/surface.ts

constructor(options: Partial<ISurfaceOptions> = {}) {
    // 1. 创建视口
    this._viewport = new Viewport();
    this._viewport.initialize(new PageVm(), renderOptions);
    
    // 2. 创建上下文
    this._context = new Context({
        pixelRatio: window.devicePixelRatio,
    });
    
    // 3. 创建视图引擎
    this.vmEngine = new VmEngine({
        context: this.context,
    });
    
    // 4. 创建事件边界(碰撞检测)
    this.eventBoundary = new EventBoundary({
        viewport: this.viewport,
    });
    
    // 5. 创建插件系统
    this._plugin = new PluginSystem({
        viewport: this.viewport,
        vmEngine: this.vmEngine,
        context: this.context,
        eventBoundary: this.eventBoundary,
    });
    
    // 6. 启动渲染循环
    this._context.ticker.add((t) => {
        this.viewport.app.ticker.update();
    }, UPDATE_PRIORITY.LOW);
}

提交变更

typescript
// 处理数据变更
commit(action: Action): void {
    this.processor.process(action);  // 处理 Action
    this.context.emit('action', action);  // 触发事件
}

碰撞检测

typescript
// 判断点击命中哪个元素
hitTest(point: IPointData): BaseElementModel | null {
    const target = this.eventBoundary.hitTest(point);
    return target ? target.getModel() : null;
}

2.9 与传统编辑器的本质区别

维度传统 DOM 编辑器无限画布编辑器
渲染方式浏览器渲染管线WebGL 直接绘制
坐标系统CSS 像素自定义世界坐标
缩放实现transform: scale()矩阵变换
事件处理DOM 事件冒泡自定义碰撞检测
性能瓶颈DOM 节点数量GPU 填充率
内存模型DOM Tree对象池 + 纹理缓存

本质区别:无限画布接管了浏览器的渲染职责,自己实现了一套「迷你浏览器」。


2.10 本章小结

  1. 无限画布:可无限延伸、自由缩放的 2D 编辑界面
  2. DOM 瓶颈:节点数量增加时性能急剧下降
  3. WebGL 优势:批量渲染、GPU 加速
  4. Piso:基于 PixiJS 的定制渲染引擎
  5. 三层架构:Surface → Viewport/VmEngine/Processor/Plugin
  6. 数据流:Model → Action → Vm → Render → GPU

下一章第3章:坐标系统与矩阵变换 —— 理解无限画布的数学基础

如有转载或 CV 的请标注本站原文地址