项目中会有2D画布上的绘制需求,原有主项目是基于原生Canvas二次封装去满足需求,然后之前有一个绘制插件的需求,就尝试用了一下Pixi.js 感觉效果还不错。前段时间因为项目需要进行重构,其中2D图形绘制的部分需要进行技术选型,刚好在这里进行记录下

在前端绘制图形,​​Fabric.js​​、​​Konva.js​​、​​Pixi.js​​ 和原生 ​​Canvas API​​ 是最常被提及的四个。首先要明确,其他三个库都是建立在 ​​Canvas API​​ 之上的抽象层。理解 Canvas 是理解它们的基础。

一、 原生 Canvas API

  • Canvas 是​​立即模式​​的图形接口。这意味着你发出绘制命令(如 ctx.fillRect(0, 0, 100, 100))的瞬间,像素就被直接绘制到了画布上。画布不会记住这个“矩形对象”,它只知道最终的像素结果。你想移动这个矩形?最简单的办法就是:

    • clearRect(...)清除画布(或一部分)。

    • 重新计算新位置。

    • 再次调用 fillRect(...)在新位置绘制。

  • 在Canvas 的规范中可以看到,它提供的是一系列非常底层的方法(fill, stroke, drawImage)。没有“场景图”(Scene Graph),没有“对象树”,所有交互、动画、状态管理这些高级概念都需要开发者手动实现,通常伴随着大量的数学计算和性能优化工作。

二、面向交互的图形对象库 (Fabric.js & Konva.js)

Fabric 和 Konva 走了同一条路:​​在立即模式的 Canvas 之上,模拟了一个保留模式 (Retained Mode) 的图形对象模型​​。这是它们最核心的贡献。

1. 核心思想:对象模型与场景图​

它们将每个图形(矩形、圆形、路径等)都包装成一个 JavaScript 对象实例。这个对象拥有自己的属性(left, top, angle, fill)。你不再直接绘制,而是操作这些对象的属性,然后由库负责​​自动、高效地将整个场景同步到 Canvas 上​​。

​2. Fabric.js 地址

  • 提供极其丰富和便捷的上层 API,让构建复杂的图形应用(如设计工具)变得快速。其源码 (src/) 结构清晰地反映了这一点,有 canvas.class.js, object.class.js等,构建了一个完整的面向对象体系。

亮点​​:

  • 序列化​​:是 Fabric 的王牌功能。任何一个 fabric.Object都实现了 toObjecttoJSON方法。整个画布的状态可以轻松导出为 JSON,并能完美加载回来。这对于协作、保存、撤销重做至关重要。

  • 事件系统​​:其事件系统直接绑定在对象上。源码中的 __onMouseDown, __onMouseMove等方法实现了复杂的点击检测(findTarget),它会遍历所有对象,根据它们的形状和变换矩阵来计算鼠标是否命中。这为你省去了海量的数学计算。

  • 交互​​:内置了操控逻辑(如控制角、边框),其 controls.js文件定义了如何绘制和交互这些控制点。

3.Konva.js 地址

  • 设计哲学​​:强调通过 ​​Stage -> Layer -> Group -> Shape​​ 的层级树(场景图)来组织内容。这种结构带来了显著的性能优势。

  • 亮点​​:

    • 分层渲染​​:这是 Konva 与 Fabric 在架构上的一个关键区别。每个 Layer都是一个独立的 Canvas 元素。当你移动一个形状时,Konva 不需要重绘整个舞台,只需要重绘该形状所在的​​图层​​。对于静态背景和动态 foreground 分离的场景,性能极佳。

    • 命中图​​:Konva 为了实现更精确的点击检测,可能会为复杂的形状(如自定义 Shape)维护一个离屏的“命中 Canvas”,用于进行像素完美的点击检测,这体现了其对交互准确性的重视。类似思想在我这篇文章有些过简单的实现

    • 性能优化​​:其场景图结构使得它可以更精细地进行“脏矩形”渲染(只重绘发生变化的部分区域),虽然 Fabric 也有类似优化,但 Konva 的层级设计使其更为自然。

​Fabric vs Konva 小结​​:

两者功能高度重叠。Fabric 更偏向于“功能齐全”,API 更偏向于直接操作对象;Konva 更偏向于“结构清晰”,API 更反映其层级树状结构。选择哪一个通常是风格和特定功能需求问题。

三、面向渲染的图形引擎 (Pixi.js)

Pixi.js 选择了另一条路:​​它不关心复杂的对象交互,它的核心目标是榨干 GPU 的性能,用来绘制大量动态的视觉元素。​

1. 核心思想:WebGL 抽象与精灵渲染​

  • Pixi 的核心是渲染器 (Renderer)。它默认使用 ​​WebGL​​,仅在旧浏览器上回退到 Canvas。WebGL 允许它使用 GPU 进行硬件加速渲染,这对于绘制大量纹理(图片)是巨大的优势。

2. 亮点

  • 渲染循环​​:Pixi 有一个核心的 Ticker(时钟),驱动着整个渲染循环。它追求的是稳定的 60FPS。

  • 精灵 (Sprite)​​:Pixi 的基本单位是 Sprite,它是对一个纹理(Texture)区域的引用。渲染数千个精灵是它的看家本领,因为它可以将这些精灵合并到几个大的 WebGL 绘制调用中(通过 batchRenderer),极大减少了 CPU 到 GPU 的通信开销。这是其性能的关键。

  • ​场景图​​:Pixi 也有 ContainerSprite组成的场景图,但这主要是为了处理变换(位置、旋转、缩放)层级关系,其设计初衷​​并非为了精细的交互​​。

  • 与 Fabric/Konva 的根本区别​​:Fabric 和 Konva 的核心是 ​​Canvas 2D Context​​,而 Pixi 的核心是 ​​WebGL Shader​​。这意味着 Pixi 可以轻松实现炫酷的滤镜(Filter),因为滤镜本质上是片段着色器(Fragment Shader),直接运行在 GPU 上。但在 Pixi 中绘制一个矢量圆,反而需要先转换成纹理,这并非其长处。

特性

Canvas (原生 API)

Fabric.js

Konva.js

Pixi.js

​层级​

​底层基础​

​高级抽象库​

​高级抽象库​

​渲染引擎​

​核心优势​

完全控制像素

​丰富的对象交互​

​分层的场景图与交互​

​极致渲染性能​

​渲染方式​

Canvas 2D

Canvas 2D

Canvas 2D

​WebGL (为主)​

​对象模型​

有 (场景图)

有 (场景图,但针对精灵)

​交互支持​

需手动实现,极其复杂

​极好​​ (拖拽、缩放、旋转)

​极好​

基础 (点击、移动),非核心

​序列化​

需手动实现

​极好​​ (JSON)

​极好​​ (JSON)

需手动或通过插件

​性能场景​

像素操作、图像处理

中等复杂度的交互应用

中等复杂度的交互应用

​大量动态元素、游戏​

​主要用途​

基础绘图、特效、图像处理

​图形编辑器、设计工具​

​交互式图表、应用​

​游戏、高性能动画​

​学习曲线​

低(基础绘图)高(高级应用)

中等

中等

中等偏上