fabric.jsで画像編集



今回は画像編集を可能にするfabric.jsを使って画像を編集する方法を記事にまとめました。
デモはこちら。

https://stackblitz.com/edit/vue-9m2br2?file=src/App.vue

この記事では以下５つを紹介します。
① 任意の画像をcanvasに読み込む
② 画像上に四角形 / 円形 / 文字を挿入する
③ 「②」の要素を削除する
④ 「②」の要素のプロパティを更新する(カラー変更・透明度・borderの太さ　など)
⑤ 読み込み画像と共に新規でデータをダウンロードする



それ以外の詳しい処理やプロパティの操作は下記の公式Docsを読んでみてください。
http://fabricjs.com/docs/fabric.Canvas.html




# 開発環境
・Javascript
・Vue.js 2系

fabric.js インストール方法

```
$ npm i fabric
```


# ① 任意の画像をcanvasに読み込む
画像を読み込むには画像パスが必要です。
fabric.jsでは```fabric.Image.fromURL```で画像パスからデータを読み込むことができます。
今回は画像を編集したいので、描画される画像をbackgroundとして使います。
その場合、```setBackgroundImage```で描画できます。

```vuejs

<template>
  <div class="hello">
    <canvas id="canvas"></canvas>
  </div>
</template>

<script>
import { fabric } from 'fabric';
export default {
  name: 'HelloWorld',
  props: {
  },

  data:() => {
    return {

    }
  },

  methods:{

    // 初回マウント
    init(){
      const canvas = new fabric.Canvas('canvas');
      canvas.selection = true;

      // 画像読込み
      const img_path = 'https://vuejs.org/images/logo.png';
      fabric.Image.fromURL(img_path,(img) => {
        canvas.setDimensions({
          width: img.width,
          height: img.height
        });
        canvas.setBackgroundImage(img,() => {
          canvas.requestRenderAll();
        });
      })
      console.log(canvas);
    }
  },
  mounted(){
    this.init();
  }

}
</script>

```
画像のwidth やheightは```setDimensions```で定義します。



# ② 画像上に四角形 / 円形 / 文字を挿入する

fabric.jsでは図形と文字を以下のように定義します。

```javascript
// オブジェクトの定義
let newRect = fabric.Rect; // 四角形
let newCircle = fabric.Circle; // 円形
let newText = fabric.Text; // 文字
```
読み込んだ画像上に図形や文字を挿入する時はまずfabric.jsのイベント処理とoptionsを理解する必要があります。

###### イベント処理
fabric.jsではユーザーアクションに応じて処理を定義できます。
たとえば、マウスクリック / クリックしたまま動かす / クリックを外す / 二重クリック　などです。

マウスクリック = ```canvas.on('mouse:down'() => {}); ```
クリックしたまま動かす = ```canvas.on('mouse:move',() => {}); ```
クリックを外す = ```canvas.on('mouse:up',() => {});```
ダブルクリック = ```canvas.on('mouse:dblclick',() => {});```

こんな感じでイベント処理が用意されています。


###### options
optionsはユーザーが行ったイベントの情報を持っています。
たとえば、クリック位置は```options.pointer```で、クリックした場所にオブジェクトがあるかどうかは```options.target```などで取得できます。

この２つを使うことでどの位置にどんな図形や文字を挿入するかを定義することができます。
たとえば赤枠の四角形を挿入したい時は

```javascript

canvas.on('mouse:down',(options) => {
      if (!options.pointer) return
      if (options.target == null) {
          newRect = new fabric.Rect({
            left: options.pointer.x,
            top: options.pointer.y,
            width: 0,
            height: 0,
            fill: 'white',
            opacity: 0.5,
            stroke: 'red',
            strokeWidth: 2,
            strokeUniform: true,
          });
       } 
});

canvas.on('mouse:move', (options) => {
   if (!options.pointer) return;
   if (newRect && newRect.left && newRect.top) {
      newRect.width = Math.max(options.pointer.x - newRect.left, 0)
      newRect.height = Math.max(options.pointer.y - newRect.top, 0)
      canvas.requestRenderAll();
   }
});

canvas.on('mouse:up', (options) => {
  if (!options.pointer) return;
  if (newRect && newRect.width != 0 && newRect.height != 0) {
   canvas.add(newRect);
  }

　　　　// 初期化
　　　　newRect = undefined;

});
```

とすれば四角形を挿入できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596022/1d846de9-080e-5677-dceb-babe558f64f3.png)


図形を```add()```した後はオブジェクトの初期化を忘れずに。
```newRect = undefined;```とすると、２個目のオブジェクトを生成できます。
円形や文字も同じです。参考パターンはデモを見てください。








# ③ 「②」の要素を削除する
要素を削除するには```remove()```メソッドを使います。

###### オブジェクトの取得
ユーザーがどの図形や文字をクリックしたか、を取得するには```canvas.getActiveObject()```を使います。```console.log()```で確認するといろんな情報が取得できます。
※ たとえば、クリックしたオブジェクトの図形タイプを取得したい場合```canvas.getActiveObject().type```で取得できます。これを利用すると各オブジェクトに応じて必要な処理を分岐できます。


以下では、特定の要素をクリック後deleteキーをタイプしたときに削除が実行されます。

```javascript
// 特定要素の削除
document.addEventListener("keyup", function (e) {
    if (e.keyCode == 8 |  e.keyCode == 46) {  // delete と backspaceに対応
      canvas.remove(canvas.getActiveObject());
    }
});
```

```remove()``` にまるっと選択オブジェクトを入れることで削除が実行されます。







# ④ 「②」の要素のプロパティを更新する(カラー変更・透明度・borderの太さ　など)
オブジェクトプロパティを更新するには```set()```メソッドと```renderAll()```を使います。

②で紹介した図形のプロパティはそれぞれ定義するものが違います。
たとえばプロパティに```fill```があり、図形だと「塗りつぶし」にあたりますが文字だとtext-colorになります。詳しい内容は公式Docsを読んでみてください。

今回カラーピッカーはHTMLの``` <input type="color">```、オパシティの透明度は``` <input type="range">```を使うことにします。

下記では四角形の塗りつぶし```fill```と透明度```opacity```を変更してみます。


```vuejs
<template>
  <div class="hello">
    <input id="defined_color" type="color">
    <input id="defined_opacity" type="range">
    <canvas id="canvas"></canvas>
  </div>
</template>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596022/d9ad6694-00fe-1bf2-c0d1-8aa376c6df91.png)


上記のinputのvalueは```.value```で取得できます。以下は値変更時の例です。

```javascript
  const defined_color = document.getElementById('defined_color');
  var d_color;
  defined_color.addEventListener('change',(e) => {
   d_color = defined_color.value;
  });

  const defined_opacity = document.getElementById('defined_opacity');
  var d_opacity;
  defined_opacity.addEventListener('change',(e) => {
    if(defined_opacity.value != 0){
     d_opacity = Math.floor(defined_opacity.value) / 100;
     console.log(d_opacity);
    } else {
     d_opacity = 0;
    }
   });
```


プロパティ変更はダブルクリックとしてみます。

```javascript
canvas.on('mouse:dblclick',(options) => {
   canvas.getActiveObject().set("fill", d_color);
   canvas.getActiveObject().set("opacity",d_opacity);

   canvas.renderAll();
});
```
これで特定のオブジェクトのプロパティを変更できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596022/c4cc884f-d7aa-42d2-d876-c299a019d477.png)

borderの太さは```strokeWidth```を、borderカラーは```stroke```プロパティを調整すると変えられます。



# ⑤ 読み込み画像と共に新規でデータをダウンロードする
読み込んだ画像を編集して新しい画像として保存したい時は```toDataURL()```を使います。

通常downnload機能を実装するには```<a href="example.gif" download="デモファイル">ダウンロード</a>```という形で実装しますね。今回は画像をデータをhrefに、downloadには画像名をセットします。

> 以下ローカルサーバー上の挙動です。サーバーを立ち上げていないと```Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.```とCrossOriginで弾かれるので注意してください。

```vuejs
<template>
  <div class="hello">
    <input id="defined_color" type="color">
    <input id="defined_opacity" type="range">
    <canvas id="canvas"></canvas>
    <a id="download">Download</a> //こいつを追加
  </div>
</template>

<script>
.
.
.

methods:{

    // 画像読込み
    init(){
      const canvas = new fabric.Canvas('canvas');
      canvas.selection = true;

      // ダウンロードボタン
      let download_btn = document.getElementById('download'); // 追加

.
.
.

　　　　　　　　　　　　// ダウンロード
      download_btn.addEventListener('click',() => {
         if (!canvas) return
         canvas.selection = true;

        console.log(canvas.toJSON());

       const image = canvas.toDataURL({ format: 'png' }).replace('image/png', 'image/octet-stream');
       download_btn.setAttribute('href',image);
       download_btn.setAttribute('download','sample.png');
      　});

.
.
.
</script>

```

```download_btn```がクリックされたときに```toDatURL({ format: 'png' })```とし、replace()で置き換えています。
こうすることでaタグのhrefにセットしたのちにダウンロードできます。今回はファイル名を'sample.png'としていますが、webアプリケーション上であればDBのデータ名をそのままセットすることもできます。



#　まとめ
今回はfabric.jsを使って画像編集を紹介しました。
オブジェクトを取得するだけでfabricを使って多彩な機能で編集できるので極めると面白そうですね。
暇があればごりごりの編集アプリでも作ってみようと思います。