Fabric.js Document Engine

搜索文档

查找页面或章节

EN

编辑器教程

查看 Markdown

从空白页面开始,一步步做一个小型画布编辑器。

这篇 Fabric.js 使用教程会带你用 TypeScript 做一个小型画布编辑器。它可以添加图形、撤销重做、自动保存,还能导出 PNG。每一步只加一个功能,所以你在任何一步停下来,代码都能运行。

如果你用 React,也建议先读这篇。React 指南用的是同样的引擎调用。

1. 页面

先准备一个画布和几个按钮:

<canvas id="editor"></canvas>
<div class="toolbar">
  <button id="add">Add square</button>
  <button id="undo">Undo</button>
  <button id="redo">Redo</button>
  <button id="export">Export PNG</button>
  <span id="status" aria-live="polite"></span>
</div>

2. 画布和引擎

像平时一样创建 Fabric.js 画布,然后把引擎连上去:

import { Canvas, Rect } from "fabric";
import { createDocumentEngine } from "fabricjs-document-engine";
import { createLocalStorage } from "fabricjs-document-engine/storage";
 
const canvas = new Canvas("editor", {
  width: 800,
  height: 500,
  backgroundColor: "#ffffff",
});
 
const engine = createDocumentEngine({
  canvas,
  storage: createLocalStorage({ prefix: "tutorial:" }),
  autosave: { delay: 1000 },
  document: { id: "my-drawing" },
});

storage 告诉引擎文档存在哪里。autosave 会在用户停止编辑一秒后保存。

3. 添加图形

添加对象时 Fabric.js 会触发事件,所以引擎会自动把它记成一步撤销:

document.querySelector("#add")!.addEventListener("click", () => {
  canvas.add(
    new Rect({
      left: 50 + Math.random() * 600,
      top: 50 + Math.random() * 350,
      width: 80,
      height: 60,
      fill: "#e76f51",
    })
  );
});

4. 撤销和重做

把按钮连到 undo() 和 redo(),再用 history:change 事件让按钮状态保持同步:

import { bindKeyboardShortcuts } from "fabricjs-document-engine";
 
const undoButton = document.querySelector<HTMLButtonElement>("#undo")!;
const redoButton = document.querySelector<HTMLButtonElement>("#redo")!;
 
undoButton.addEventListener("click", () => engine.undo());
redoButton.addEventListener("click", () => engine.redo());
 
engine.on("history:change", ({ canUndo, canRedo, undoLabel }) => {
  undoButton.disabled = !canUndo;
  redoButton.disabled = !canRedo;
  undoButton.textContent = undoLabel ? `Undo ${undoLabel}` : "Undo";
});
 
bindKeyboardShortcuts(engine);

现在 Ctrl+Z 和 Cmd+Z 可以撤销,Ctrl+Shift+Z 可以重做。用鼠标移动或缩放图形,也各算一步。

5. 自动保存和保存状态

自动保存已经在运行了。把它的状态显示出来,并在页面加载时打开之前保存的画:

const status = document.querySelector("#status")!;
 
engine.on("save:status", ({ status: state }) => {
  status.textContent = state === "saved" ? "All changes saved" : state;
});
 
await engine.load("my-drawing").catch(() => {
  // Nothing saved yet: start with the empty document.
});

刷新页面,图形还在。

6. 导出 PNG

export() 会先检查图片和字体,再渲染。downloadExport() 负责触发下载:

import { downloadExport } from "fabricjs-document-engine";
 
document.querySelector("#export")!.addEventListener("click", async () => {
  const result = await engine.export({ format: "png", scale: 2 });
  downloadExport(result, "drawing.png");
});

你做出了什么

大约 60 行代码,这个 Fabric.js 使用教程里的画布编辑器就能:

  • 添加图形,并撤销或重做每一步操作;
  • 自动保存到 localStorage,刷新后重新打开;
  • 以两倍尺寸导出 PNG。

下一步

  • 保存冲突:两个标签页编辑同一份文档时会怎样。
  • 恢复:崩溃之后找回未保存的内容。
  • 自定义后端:保存到你自己的 API,而不是 localStorage。