Fabric.js Document Engine

搜索文档

查找页面或章节

EN

一个带撤销、重做和实时保存状态的 Svelte 组件。

Fabric.js Svelte 组件要在 onMount 里创建画布,因为那时 <canvas> 元素才存在。createDocumentStateStore 本身就有 subscribe 方法,所以把它变成 Svelte store 只要一行。

组件

<script lang="ts">
  import { Canvas, Rect } from "fabric";
  import {
    createDocumentEngine,
    createDocumentStateStore,
    type DocumentEngine,
  } from "fabricjs-document-engine";
  import { onMount } from "svelte";
  import { readable, type Readable } from "svelte/store";
 
  let element: HTMLCanvasElement;
  let engine: DocumentEngine | undefined;
  let state: Readable<ReturnType<ReturnType<typeof createDocumentStateStore>["getSnapshot"]>> | undefined;
 
  onMount(() => {
    const canvas = new Canvas(element, { width: 800, height: 500 });
    engine = createDocumentEngine({ canvas });
    const store = createDocumentStateStore(engine);
 
    state = readable(store.getSnapshot(), (set) =>
      store.subscribe(() => set(store.getSnapshot()))
    );
 
    return () => {
      store.destroy();
      engine?.destroy();
      canvas.dispose();
    };
  });
 
  function addSquare() {
    engine?.canvas.add(new Rect({ width: 80, height: 60, fill: "#e9c46a" }));
  }
</script>
 
<div role="toolbar">
  <button on:click={addSquare}>Square</button>
  <button disabled={!$state?.canUndo} on:click={() => engine?.undo()}>
    Undo {$state?.undoLabel ?? ""}
  </button>
  <button disabled={!$state?.canRedo} on:click={() => engine?.redo()}>Redo</button>
</div>
<canvas bind:this={element} aria-label="Drawing canvas"></canvas>

store 怎么工作

readable(initial, start) 会在第一个组件订阅时调用 start,在最后一个组件离开时调用它返回的函数。store.subscribe 返回的正好就是取消订阅的函数,所以 Svelte store 和引擎的 store 不用额外代码就能保持同步。

SvelteKit

Fabric.js 需要浏览器。在 SvelteKit 里,onMount 只在浏览器中运行,所以上面的 Fabric.js Svelte 代码在服务端渲染时是安全的。如果你在共享模块顶部导入了 Fabric.js,请把这个导入移进组件里。

要保存,请给 createDocumentEngine 加上存储适配器。原生 JavaScript 指南展示了不用框架时同样的引擎调用。