Fabric.js
Fabric.js 是一个老牌且功能非常强大的 Canvas 2D 库。
为什么选择 Fabric.js 开发 Canvas 项目?
在开发复杂的 Canvas 项目(如在线设计工具、白板、海报编辑器等)时,直接使用原生 Canvas API 往往会陷入无尽的底层坐标计算和状态管理中。选择 Fabric.js 主要是为了解决原生开发的痛点,其核心理由和优势包括:
- 对象化管理(告别原生像素操作):原生 Canvas 是“画完即忘”的指令式绘制,无法单独修改已画好的元素。Fabric.js 封装了一套类似 DOM 树的对象模型,开发者可以像修改普通对象一样去修改图形的属性(如
width、left、fill),它会自动处理复杂的画布重绘逻辑。 - 开箱即用的高级交互:用原生 API 从零实现一套图形的选中、拖拽、旋转、按比例缩放(包括外围控制点和边框)需要极高的数学(矩阵、几何)功底和成百上千行的代码。Fabric.js 直接内置了这些能力,极大降低了研发成本。
- 完美的序列化与反序列化:业务项目中“保存图纸”、“加载模板”是刚需。Fabric.js 支持一键将画布内容导出为 JSON 数据 (
canvas.toJSON()),并能无损还原 (canvas.loadFromJSON()),非常便于做数据存储和撤销重做(Undo/Redo)功能。 - 强大的 SVG 兼容性:在众多图形库中,Fabric.js 对 SVG 格式的解析和双向转换支持是业界最成熟的之一。你可以轻松将外部 SVG 导入为画布对象,或将画布导出为高保真 SVG 文件。
- 细粒度的事件系统:原生 Canvas 只能给整个画布
<canvas>绑定点击事件并自己计算点中了什么。而 Fabric.js 实现了对象级别的事件监听,可以精确监听到某个特定图形的mousedown、moving、scaled、modified等动作,让业务逻辑与视图解耦。
核心特性与底层原理
对象模型 (Object Model) 与虚拟树: 原生 Canvas 只有“像素”没有“对象”,画完即忘。Fabric.js 在内部维护了一棵对象树(类似 DOM 树)。它的所有图形(
fabric.Rect,fabric.Circle)都继承自fabric.Object,拥有top,left,angle,scaleX等属性。当属性改变时,Fabric.js 会调用内部的renderAll()方法,自动擦除画布并遍历对象树重新绘制。双层 Canvas 架构 (Upper & Lower Canvas):
面试题:在 Fabric.js 里拖拽一个矩形时,为什么底下的几千个图形没有因为重绘而卡顿?
- 底层原理:当你实例化一个
new fabric.Canvas('c')时,Fabric.js 会在 DOM 里动态创建两个重叠的 Canvas。 - Lower Canvas(底层):负责渲染所有的静态图形对象。
- Upper Canvas(上层):专门负责交互与事件(捕获鼠标事件,绘制选中时的虚线控制框、拖拽时的影子等)。
- 收益:拖拽图形时,底层的 Canvas 完全不重绘,只有上层的 Canvas 在极小范围内重绘拖拽框,这是其在复杂交互下不卡顿的核心秘密。
- 底层原理:当你实例化一个
几何射线拾取机制 (Raycast / Point in Polygon): 不同于 Konva 的颜色拾取,Fabric.js 的事件拾取依赖于纯数学几何计算。
- 当鼠标点击时,它会先通过
AABB(Axis-Aligned Bounding Box,轴对齐包围盒)做一次粗筛,快速剔除绝对点不到的图形。 - 粗筛通过后,再利用射线法(Ray-casting)计算坐标是否在多边形的轮廓内部,或者利用点到直线的距离判断是否点中了线段。
- 当鼠标点击时,它会先通过
SVG 与 Canvas 双向转换: 这是 Fabric.js 最杀手级的特性。它可以轻松地把 SVG 字符串或文件解析成 Canvas 对象树,也能把画布内容随时序列化导出为 SVG 或 JSON(
canvas.toJSON())。这在海报设计、T恤定制等场景中极其重要。
实战代码示例
import { Canvas, Rect, Circle } from "fabric";
// 1. 初始化,此时会自动生成上下两层 Canvas
const canvas = new Canvas("myCanvas");
// 2. 创建对象
const rect = new Rect({
left: 100,
top: 100,
fill: "red",
width: 20,
height: 20,
selectable: true, // 开启拖拽控制
});
// 3. 加入画布(触发底层 Canvas 绘制)
canvas.add(rect);
// 4. 事件监听(由上层 Canvas 捕获)
rect.on("selected", function () {
console.log("矩形被选中了!");
});
适用场景
- 在线图片编辑器、海报生成器(类似 Canva 的核心基础)。
- 需要频繁导出/导入 SVG 和 JSON 的业务。