Closed7

[キャッチアップ] Fabric.js を Vue3 から使う

shingo.sasakishingo.sasaki

概要

  • fabric.js を Vue3 で使ってゴニョゴニョする際に自分用メモです
  • fabric 自体初めて使うので、そっちに関するメモも多めです
  • 知見が溜まったら記事にまとめるかも
shingo.sasakishingo.sasaki

セットアップ

vue-cli でひな形作る
preset は vue3 + TypeScript さえあればOKなので適当に

$ vue create project-name

facric 及び型情報のインストール

$ yarn add facric
$ yarn add -D @types/fabric
shingo.sasakishingo.sasaki

Hello, Fabric

App.vue を編集して、 Fabric が最低限動くことを確認する

src/App.vue
<template>
  <h1>Hello, Facric!!</h1>
  <button @click="add">Add</button>
  <div class="wrapper">
    <canvas id="canvas" width="600" height="600" />
  </div>
</template>

<script lang="ts">
import { computed, defineComponent } from "vue";
import { fabric } from "fabric";

export default defineComponent({
  setup() {
    const canvas = computed<fabric.Canvas>(() => new fabric.Canvas("canvas"));

    const add = () => {
      canvas.value.add(
        new fabric.Rect({
          top: 100,
          left: 100,
          width: 60,
          height: 60,
          fill: "red",
        })
      );
    };

    return { add };
  },
});
</script>

<style scoped>
.wrapper {
  width: 600px;
  height: 600px;
  border: 1px solid;
}
</style>

fe1314df53be1bc79fb66d5549b1b748e2a8c736

良い感じに、ボタンを押すと移動したり拡大縮小できる矩形オブジェクトがキャンバスに描画されるようになった。

shingo.sasakishingo.sasaki

LocalStorage で状態を保存、復元する

Canvas オブジェクトに対して、 JSON.stringify してあげれば状態をJSONで取得でき、 loadFromJSON メソッドで復元することができる。

ボタンを適当に追加して

src/App.vue
<template>
  <h1>Hello, Fabric!!</h1>
  <button @click="add">Add</button>
  <button @click="save">Save</button>
  <button @click="load">Load</button>
  <div class="wrapper">
    <canvas id="canvas" width="600" height="600" />
  </div>
</template>

save / load の関数を作成

src/App.vue
export default defineComponent({
  setup() {
    // 中略
    const save = () => {
      const canvasState = JSON.stringify(canvas.value);
      localStorage.setItem("state", canvasState);
    };

    const load = () => {
      const canvasState = localStorage.getItem("state");
      canvas.value.loadFromJSON(canvasState, () => console.log("loaded!!"));
    };

    return { add, save, load };
  },
});

9e4d0fb7f8fd59ff6b87c504567d8838c545102b

shingo.sasakishingo.sasaki

背景画像を設定する

fabric.Image.fromURL で、URLから画像を取得できるので、それをキャンバスいっぱいに引き伸ばして微妙がする。(例として zenn のユーザーアイコン画像を使わせてもらってます)

src/App.vue
    onMounted(() => {
      const imageUrl = 'https://storage.googleapis.com/zenn-user-upload/avatar/845fa75ba8.jpeg'
      fabric.Image.fromURL(imageUrl, image => {
        image.scaleToWidth(canvas.value.getWidth())
        canvas.value.setBackgroundImage(image, canvas.value.renderAll.bind(canvas.value))
      })
    })

3b72182178b0ac0aac32a0b3f353c4521c52a21e

Kawaii

shingo.sasakishingo.sasaki

矩形オブジェクト用のコンポジションを切り出す

ここまでは new fabric.Rect を使って、初期値が固定の矩形オブジェクトを生成していたが、これをコンポジションの分離する。

まずは丸ごと移動して、 fabric をラップしてくれる形にすることで、コンポーネント側をシンプルにする

src/compositions/useFabric.ts
import { fabric } from 'fabric'
import { IRectOptions } from 'fabric/fabric-impl'
import { computed } from 'vue'

export default function useFabric(canvasId: string) {
  const canvas = computed<fabric.Canvas>(() => new fabric.Canvas(canvasId))

  const addObject = (object: fabric.Object) => {
    canvas.value.add(object)
  }

  const addRect = (options: IRectOptions) => {
    const rect = new fabric.Rect(options)
    addObject(rect)
  }

  const setBackgroundImageFromUrl = (url: string) => {
    fabric.Image.fromURL(url, image => {
      image.scaleToWidth(canvas.value.getWidth())
      canvas.value.setBackgroundImage(image, canvas.value.renderAll.bind(canvas.value))
    })
  }

  const fromJSON = (json: string | null) => {
    canvas.value.loadFromJSON(json, () => {})
  }

  const toJSON = () => {
    return JSON.stringify(canvas.value)
  }

  return { addRect, setBackgroundImageFromUrl, fromJSON, toJSON }
}
src/App.vue
export default defineComponent({
  setup() {
    const fabric = useFabric('canvas')

    onMounted(() => {
      const imageUrl = 'https://storage.googleapis.com/zenn-user-upload/avatar/845fa75ba8.jpeg'
      fabric.setBackgroundImageFromUrl(imageUrl)
    })

    const add = () => {
      fabric.addRect({ width: 100, height: 100, fill: 'red' })
    }

    const save = () => {
      localStorage.setItem('state', fabric.toJSON())
    }

    const load = () => {
      fabric.fromJSON(localStorage.getItem('state'))
    }

    return { add, save, load }
  }
})

aeb20c5c35d4e037731678778631db6c88313ccb

shingo.sasakishingo.sasaki

選択中のオブジェクトを削除する

src/compositions/useFabric.ts
  const removeObject = (object: fabric.Object) => {
    canvas.value.remove(object)
  }

  const removeSelectedObject = () => {
    removeObject(canvas.value.getActiveObject())
  }
このスクラップは2021/04/12にクローズされました