Fabric.js Document Engine

Search documentation

Find a page or section

中文

A Svelte component with undo, redo and a live save status.

A Fabric.js Svelte component creates the canvas in onMount, because the <canvas> element exists only then. createDocumentStateStore already has a subscribe method, so turning it into a Svelte store takes one line.

The component

<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>

How the store works

readable(initial, start) calls start when the first component subscribes, and the function it returns when the last one leaves. store.subscribe returns exactly that unsubscribe function, so the Svelte store and the engine store stay in sync without extra code.

SvelteKit

Fabric.js needs a browser. In SvelteKit, onMount only runs in the browser, so the Fabric.js Svelte code above is safe during server rendering. If you import Fabric.js at the top of a shared module, move that import into the component.

For saving, add a storage adapter to createDocumentEngine. The plain JavaScript guide shows the same engine calls without a framework.