Fabric.js, Konva.js, Pixi.js 与 Canvas:为何同源不同路?
项目中会有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都实现了toObject和toJSON方法。整个画布的状态可以轻松导出为 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 也有
Container和Sprite组成的场景图,但这主要是为了处理变换(位置、旋转、缩放)层级关系,其设计初衷并非为了精细的交互。与 Fabric/Konva 的根本区别:Fabric 和 Konva 的核心是 Canvas 2D Context,而 Pixi 的核心是 WebGL Shader。这意味着 Pixi 可以轻松实现炫酷的滤镜(Filter),因为滤镜本质上是片段着色器(Fragment Shader),直接运行在 GPU 上。但在 Pixi 中绘制一个矢量圆,反而需要先转换成纹理,这并非其长处。