Angularアプリでお絵描きする方法です。
HTMLのcanvasをJavascriptから扱えるFabric.js
というライブラリがあったので、これと組み合わせて実現してみました。
Fabric.jsは生のcanvasを扱うよりも簡単にcanvasの操作を行うことが出来る他、canvasオブジェクトのレイヤー機能や拡大縮小、回転などと言った操作が簡単に行えるので、かなり便利です。
完成形
https://daikiojm.github.io/angular-fabricjs-freehand-sample/
環境
$ ng --version
@angular/cli: 1.4.1
node: 8.1.3
os: darwin x64
上記バージョンのangular-cliで作成した、angularプロジェクトが対象です。
手順
Angular Materialの導入
ボタンなどをいい感じにスタイリングするために、Angular Materialを導入しました。以前にAngularCLIで作成したプロジェクトにAngular Materialを導入する手順をQiitaに書いたのでここを参考に作業を行います。
注意
この記事を書いている時点の最新版 2.0.0-beta.11
では、MaterialModuleが廃止されているので、Angular Materialのうち使用するコンポーネントに対応するモジュールを個別にインポートする必要があります。
https://github.com/angular/material2/pull/6803
今回は、ボタンとチェックボックスをいい感じにスタイリングしたかったので、次のモジュールをインポートしました。
MdButtonModule
MdCheckboxModule
Fabric.jsの導入
npmからインストールして、使用するコンポーネント内でimport
します。
Fabric.js のoptionalDependenciesとしてインストールされるnode-canvasのインストールになかなか時間がかかるので、今回は省略するために--no-optional
を付けています。
npm install --no-optional --save fabric
今回は、app.componentに直接実装するので、ここに記述
import { fabric } from 'fabric';
説明
canvasの作成
まずは、Viewテンプレート側でhtmlのcanvasを作成します。
<canvas id="canvas" width="600" height="600"></canvas>
app.componentにcanvasオブジェクトを宣言します。
private canvas: any;
次に、ngOnInit 内にcanvasの初期化処理を書いていきます。
isDrawingMode
オプションをつけることで、手書き線の描画が可能になります。
this.canvas = new fabric.Canvas('canvas', {
isDrawingMode: true,
selection: true,
stateful: true
});
ブラシのセットアップ
前の手順までで、canvasへの手書き描画が出来るようになっていますが、線の色や太さなどの細かい設定を行います。
this.canvas.freeDrawingBrush = new fabric.PencilBrush(this.canvas);
this.canvas.freeDrawingBrush.color = 'red';
this.canvas.freeDrawingBrush.width = 5;
this.canvas.freeDrawingBrush.shadowBlur = 0;
this.canvas.hoverCursor = 'move';
Undo/Redo機能
Fabric.jsでは、canvasオブジェクトをJavascriptのオブジェクトとして一つ一つ個別に操作することが出来るため、履歴操作も簡単に行うことが出来ました。
app.componentにcanvasHistoryを宣言します。
private canvas: any;
Fabric.jsではcanvasに対するイベントを簡単に取得することが出来るため、次のようにオブジェクトの追加を監視することが出来ます。
this.canvas.on('object:added', (e) => {
const object = e.target;
if (!this.isRedoing) {
this.canvasHistory = [];
}
this.isRedoing = false;
});
「戻る」「進む」ボタンが押されたときの操作は次のようになります。
// 戻る
undo() {
if (this.canvas._objects.length > 0) {
const undoObject = this.canvas._objects.pop();
this.canvasHistory.push(undoObject);
this.canvas.renderAll();
}
}
// 進む
redo() {
if (this.canvasHistory.length > 0) {
this.isRedoing = true;
const redoObject = this.canvasHistory.pop();
this.canvas.add(redoObject);
}
}
モード切替
消しゴム機能の実装を考えましたが、生のcanvasオブジェクトを操作しなければ出来ないようなので、今回は見送りました。
その代わり、次のように削除機能を実装しました。
Fabric.jsでは、一つ一つのオブジェクトをマウス操作で移動、拡大縮小、回転操作することが出来ますが、手書き描画が出来る状態ではそれらが行えないので、まずはそれらを切り替えるボタンを作成します。
Fabric.jsのcanvasオブジェクトのプロパティ``にViewテンプレート側からもアクセスできるよう、getter/setterを作成しました。
get drawingMode(): boolean { return this.canvas.isDrawingMode; }
set drawingMode(val: boolean) { this.canvas.isDrawingMode = val; }
Viewコンポーネント側からはmd-checkbox
を使ってモードの切替をチェックボックスのトグルで行えるようにしました。
<md-checkbox class="example-margin" [(ngModel)]="drawingMode">手書きモード</md-checkbox>
また、「手書きモード」がオフの状態では選択されたcanvasオブジェクトを削除できる機能を作成しました。次のようなメソッドを用意して、Viewテンプレート側からボタンの(click)イベントで呼び出すだけです。
deleteObject() {
const object = this.canvas.getActiveObject();
this.canvas.remove(object);
}
所感
生のcanvasを直接操作するよりかなり楽に実現できたと思います。
今回の内容では、あまりAngularと関係ない内容でしたが、機能を拡張して矩形オブジェクトの配置やペンの色変更など複雑な操作が増えるにつれてAngularと組み合わせるメリットが出てくるのでは無いかと思います。
リポジトリ