Skip to main content

Fabric.js

Fabric.js 是一个老牌且功能非常强大的 Canvas 2D 库。

为什么选择 Fabric.js 开发 Canvas 项目?

在开发复杂的 Canvas 项目(如在线设计工具、白板、海报编辑器等)时,直接使用原生 Canvas API 往往会陷入无尽的底层坐标计算和状态管理中。选择 Fabric.js 主要是为了解决原生开发的痛点,其核心理由和优势包括:

  1. 对象化管理(告别原生像素操作):原生 Canvas 是“画完即忘”的指令式绘制,无法单独修改已画好的元素。Fabric.js 封装了一套类似 DOM 树的对象模型,开发者可以像修改普通对象一样去修改图形的属性(如 widthleftfill),它会自动处理复杂的画布重绘逻辑。
  2. 开箱即用的高级交互:用原生 API 从零实现一套图形的选中、拖拽、旋转、按比例缩放(包括外围控制点和边框)需要极高的数学(矩阵、几何)功底和成百上千行的代码。Fabric.js 直接内置了这些能力,极大降低了研发成本。
  3. 完美的序列化与反序列化:业务项目中“保存图纸”、“加载模板”是刚需。Fabric.js 支持一键将画布内容导出为 JSON 数据 (canvas.toJSON()),并能无损还原 (canvas.loadFromJSON()),非常便于做数据存储和撤销重做(Undo/Redo)功能。
  4. 强大的 SVG 兼容性:在众多图形库中,Fabric.js 对 SVG 格式的解析和双向转换支持是业界最成熟的之一。你可以轻松将外部 SVG 导入为画布对象,或将画布导出为高保真 SVG 文件。
  5. 细粒度的事件系统:原生 Canvas 只能给整个画布 <canvas> 绑定点击事件并自己计算点中了什么。而 Fabric.js 实现了对象级别的事件监听,可以精确监听到某个特定图形的 mousedownmovingscaledmodified 等动作,让业务逻辑与视图解耦。

核心特性与底层原理

  1. 对象模型 (Object Model) 与虚拟树: 原生 Canvas 只有“像素”没有“对象”,画完即忘。Fabric.js 在内部维护了一棵对象树(类似 DOM 树)。它的所有图形(fabric.Rect, fabric.Circle)都继承自 fabric.Object,拥有 top, left, angle, scaleX 等属性。当属性改变时,Fabric.js 会调用内部的 renderAll() 方法,自动擦除画布并遍历对象树重新绘制。

  2. 双层 Canvas 架构 (Upper & Lower Canvas)

    面试题:在 Fabric.js 里拖拽一个矩形时,为什么底下的几千个图形没有因为重绘而卡顿?

    • 底层原理:当你实例化一个 new fabric.Canvas('c') 时,Fabric.js 会在 DOM 里动态创建两个重叠的 Canvas
    • Lower Canvas(底层):负责渲染所有的静态图形对象。
    • Upper Canvas(上层):专门负责交互与事件(捕获鼠标事件,绘制选中时的虚线控制框、拖拽时的影子等)。
    • 收益:拖拽图形时,底层的 Canvas 完全不重绘,只有上层的 Canvas 在极小范围内重绘拖拽框,这是其在复杂交互下不卡顿的核心秘密。
  3. 几何射线拾取机制 (Raycast / Point in Polygon): 不同于 Konva 的颜色拾取,Fabric.js 的事件拾取依赖于纯数学几何计算

    • 当鼠标点击时,它会先通过 AABB(Axis-Aligned Bounding Box,轴对齐包围盒)做一次粗筛,快速剔除绝对点不到的图形。
    • 粗筛通过后,再利用射线法(Ray-casting)计算坐标是否在多边形的轮廓内部,或者利用点到直线的距离判断是否点中了线段。
  4. 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 的业务。