Fabric.js Document Engine

搜索文档

查找页面或章节

EN

一个带撤销、重做和保存状态的 Vue 3 组件。

Fabric.js Vue 3 项目有一个经典 bug:把 Fabric.js 画布放进深层 ref。Vue 会把它包成响应式代理,Fabric.js 就会出现各种怪问题,比如 loadFromJSON 之后不渲染。把画布和引擎放在 shallowRef 或普通变量里,工具栏状态用 createDocumentStateStore。

组件

<script setup lang="ts">
import { Canvas, Rect } from "fabric";
import {
  createDocumentEngine,
  createDocumentStateStore,
  type DocumentEngine,
  type DocumentState,
} from "fabricjs-document-engine";
import { onBeforeUnmount, onMounted, shallowRef, useTemplateRef } from "vue";
 
const element = useTemplateRef<HTMLCanvasElement>("element");
const engine = shallowRef<DocumentEngine>();
const state = shallowRef<DocumentState>();
let cleanup = () => {};
 
onMounted(() => {
  const canvas = new Canvas(element.value!, { width: 800, height: 500 });
  const created = createDocumentEngine({ canvas });
  const store = createDocumentStateStore(created);
 
  state.value = store.getSnapshot();
  const unsubscribe = store.subscribe(() => {
    state.value = store.getSnapshot();
  });
  engine.value = created;
 
  cleanup = () => {
    unsubscribe();
    store.destroy();
    created.destroy();
    canvas.dispose();
  };
});
 
onBeforeUnmount(() => cleanup());
 
function addSquare() {
  engine.value?.canvas.add(
    new Rect({ width: 80, height: 60, fill: "#2a9d8f" })
  );
}
</script>
 
<template>
  <div role="toolbar">
    <button @click="addSquare">Square</button>
    <button :disabled="!state?.canUndo" @click="engine?.undo()">
      Undo {{ state?.undoLabel }}
    </button>
    <button :disabled="!state?.canRedo" @click="engine?.redo()">Redo</button>
  </div>
  <canvas ref="element" aria-label="Drawing canvas" />
</template>

为什么用 shallowRef

shallowRef 只在你替换 .value 时触发更新,不会包装里面的 Fabric.js 对象,所以 Fabric.js 拿到的是真实对象。state 快照每次变化都是新对象,所以 shallowRef 照样能更新模板。

画布已经是响应式的

如果 Fabric.js 画布已经放进了 reactive() 或深层 ref,用 toRaw 取出原始对象再传入:

import { toRaw } from "vue";
 
const engine = createDocumentEngine({ canvas: toRaw(canvasRef.value) });

Pinia store 也一样:不要把 Fabric.js 对象放进 store 的 state,或者用 markRaw 标记它们。

Fabric.js Vue 3 的其余配置和原生 JavaScript 一样。要保存,请加上存储适配器。