createJSでクロスドメインの画像ファイルを使った時のマウスイベントエラーへの対処

createjsでは、クロスドメインの問題で悩まされるときがあります。

その中の一つに、
「JSファイルを実行しているのと別のドメイン(画像を置いてるサーバなど)から画像をロードしたBitmap/BitmapAnimationだとマウスイベントが拾えない」
という問題があります。

ドメインの画像で作ったボタンなどをクリックするとChromeでは以下のJavascriptエラーが出ます。

Unable to get image data from canvas because the canvas has been tainted by cross-origin data.
Uncaught An error has occurred. This is most likely due to security restrictions on reading canvas pixel data with local or cross-domain images.

1行目はブラウザにより報告されているエラーです。
2行目はCreateJSにより報告されているエラーです。

createjsのソースを見ると、これは、DisplayObject.jsの_testHit()メソッドで生じていることがわかります。(easeljsバージョン0.6.0時点)

p._testHit = function(ctx) {
try {
var hit = ctx.getImageData(0, 0, 1, 1).data[3] > 1;
} catch (e) {
if (!DisplayObject.suppressCrossDomainErrors) {
throw "An error has occurred. This is most likely due to security restrictions on reading canvas pixel data with local or cross-domain images.";
}
}
return hit;
}

1行目はcanvasAPIであるgetImageData()で出ていて、2行目はthrowしているエラーメッセージです。
エラーを投げているのでここで処理が止まってしまいます。

getImageData()でエラーが発生しているのですから、suppressCrossDomainErrorsプロパティをtrueに設定しても解決しません。


蛇足ですが、この関数が何をやってるか説明すると、マウスポインタがある座標のピクセルのαが1(完全不透明が255です)より大きければヒットテストに合格した、と判定しています。
Easeljsは透過している部分をマウスイベントの対象から除外するんですよね。
>0でなく>1で判定している理由は不明です。



■対処方法
衝突判定のために衝突判定専用の表示オブジェクトを用意する、という定番の方法を利用します。
簡単なのは、ボタンと同じサイズのShapeを用意することですね。

サンプルソース

var button = new createjs.Bitmap('別ドメイン/button.png');
button.addEventListener("mousedown", buttonMouseDownHandler);
var hitObject = new createjs.Shape();
hitObject .graphics.beginFill("#000000").drawRect(0, 0, button.image.width, button.image.height);
button.hitArea = hitObject;

parentObject.addChild(button);



以下がポイントです。
①hitObjectの位置は、buttonの相対座標としてマウスイベント時に計算されます。なので、buttonと同じ位置に設定したければx=0、y=0でOK。
②マウスイベントリスナーはbuttonにつけてOKです。hitAreaに設定しているオブジェクトがあると、そちらに対して_hitTest()関数で判定されます。
③hitObjectは表示リストに加える必要がありません。なので、ステージ上には表示されません。αが不透明でありさえすれば何色であっても問題ありません。


特に、表示リストに加える必要がない!というのがhitAreaプロパティを使う利点かな、と思います。
表示リストに衝突判定専用のオブジェクトを加えるなら不透明が強制されるのは不便ですからね。