JavaScript
fabric.js

fabric.jsでの図形配置とマウスイベント

以前お話した通り、私は、javascriptとfabric.jsを使用した視覚的なプログラミング言語を開発しており、その際に培ったと言うか、苦戦しながら身につけた技術を公開します。

fabric.jsの日本語マニュアルとしては

闇猫のプログラミング工房
http://yamineko.lv9.org/
が非常にわかりやすいのだけど、あくまで初心者中級者向けで、そもそも公式サイトの翻訳が2013年で中断してますからねえ。

ちょっと凝ったことをしようと思えば、本家サイトをweb翻訳しつつトライアンドエラーの日々でした。

図形貼り付けとマウスイベントの癖

これは私がまだ勉強不足なのかfabric.jsの仕様なのかはわかりませんが、図形貼り付けおよびマウスイベントに独特の癖があります。
これは単体で図形を貼り付けて処理、あるいは貼り付けっぱなしなら何ら問題ないレベルなのですが、私が開発してる視覚的なプログラム言語を作る際に頭を悩ませる要素でした。
多分、日本では私みたいな凝った使い方した人間があまりいなかったからじゃないかなぁ。
(事実グーグル検索の質問箱で引っかかるのは海外のサイトで、翻訳で随分お世話になりました)

それでもfabric.jsありと無しでは雲泥の差です。
canvasで図形をベクターデータで扱え拡大縮小回転、SVGデータが使え重ね合わせやグループ化まで使えるというのは魅力です。

それを踏まえた上で、注意すべき「癖」について一つ一つ解説していきます。

1. データ読み込み順に描かれる。
これはfabricなしのjavascriptそのものの仕様なので仕方がないのでしょうが、
たとえ命令文で上の方に書かれていても、読み込みの早いものが先、遅いものは後から表示されます。つまり普通に表記すれば円や四角が先、svg読み込み図形が後になるわけです。
そして、後から書かれたものが重ね順の上にきます。
つまり円や四角の図形をsvg図形より上に表記しようとすれば、後述の特殊な方法を使うしかありません(少なくとも私はその方法でしか対処できてません)
また、後述のitem()を使うのにも厄介な問題が発生します。

2. マウスイベント効果がcanvas内全部に適用される。
fabric.jsの便利なのは、マウスクリックやドラックといったイベントが図形単位で発生することです。
ですがこれ、私のやりかたが悪いだけなのかもしれませんがマウスイベントが「図形のある位置かそうでないか」しか判別せず、たとえば10個の図形を配置すると、どれを押しても「マウスがクリックされた」と判別されます。
ので、おそらくは邪道と思われる特殊な方法でこれを回避してます。

3.特定の図形に対する効果の適用がほぼitem一択。

これも私のやりかたが悪いのかもですが色々試して上手く行かず、現状図形への効果適用はitem(数字)という方法を使っております。これは、画面に表示された順番に図形に番号が割り振られ、その数字を指定して色を変えたり、拡大縮小や非表示をするというものです。
で、これについては前述の1.が密接に関わっていて、ちゃんと図形の表示順ルールを把握してないと、そもそも特定の図形に効果が適応されず画面がグチャになる修羅場になります(それをまぁ、今まで何百回経験したか。そして何度パソコンを壊そうと思ったことか)

そういった癖に対応する裏技

以上の使い勝手の悪さを頭悩ませて編み出した苦肉の策が、これから紹介する裏技になります。多分、いや間違いなく公式の仕様を読み込めばもっとうまい方法があるはずですが。

1. 通常図形をSVG図形の上に表記する裏技

極めてベタな方法ですが、表示にウエイトをかけます。
具体的には後(重ね順上)に表示したい通常の図形を

setTimeout('関数', 秒数);

で遅らせることで実現します。

2. 特定の図形のマウスイベントの処理

fabric.jsにはグループ化と言う機能があり、例えば図形と文字を組み合わせて一つのオブジェクトとして扱うことが出来ます。
そしてこちらもグループ内での表示順をitem(数字)で読み書き可能です。

これを応用して、図形の中に「非表示」のテキストを埋め込み、これを変数として利用します。

例えば特定のオブジェクトをクリックした際に、

a=e.target.item(1).text;

のように、クリックした図形に埋め込まれたテキストを取り出し、
その内容でifやswitchで分岐してあげるというわけです。

同様に特定の図形にだけ効果を提供されたい場合は、まず表示順の番号で絞り込み、さらに処理図形に組み込まれてるテキストを読み込んで内容で分岐させてやれば、図形の一部、あるいは「同じ種類の図形全部」に処理を適応させることが可能になるわけです。

最後に

というのが、かれこれ2ヶ月ほどfabric.jsを弄った上での私の対処法です。
繰り返しになりますが、多分もっと良い方法があると思うので、もしそれを見つけたら教えてください(わりと切実)。