主に FlashCC からはき出した CreateJS ファイルの話です。生 CreateJS で適用する方法は下にメモってあります。
タイトルの通り、 iOS7 かつ iPhone4 という組み合わせのみ、hitArea を設定したシンボルへのイベントが走らなくなる。
createjs.Container.prototype._getObjectsUnderPoint というメソッド内の処理では、引数で受け取った x, y とステージ上の各シンボルとで hitTest を行っているのだが、その方法として 1 x 1ピクセルの canvas にそれらのシンボルを転写して、何らかの描画がされてるかどうか で判断している。
iOS7 かつ iPhone4 という組み合わせだと、この転写がうまくいっておらず何も描画されていない状態と判定されてしまい hitTest が false になる。その辺りはまだ深掘りしてない。
そもそもはブラウザ側のバグだが、どうにか逃げないといけないので、取り急ぎ下記のように逃げた。
var Container = cjs.Container;
// 元のコードをコピペしてきて、一部を書き換えてる
Container.prototype._getObjectsUnderPoint = function(x, y, arr, mouse, activeListener) {
var ctx = createjs.DisplayObject._hitTestContext;
var mtx = this._matrix;
var children = this.children;
var l = children.length;
activeListener = activeListener || (mouse&&this._hasMouseEventListener());
for (var i=l-1; i>=0; i--) {
var child = children[i];
var hitArea = child.hitArea;
if (!child.visible || (!hitArea && !child.isVisible()) || (mouse && !child.mouseEnabled)) { continue; }
// if a child container has a hitArea then we only need to check its hitArea, so we can treat it as a normal DO: > だそうです
if (!hitArea && child instanceof Container) {
var result = child._getObjectsUnderPoint(x, y, arr, mouse, activeListener);
if (!arr && result) { return (mouse && !this.mouseChildren) ? this : result; }
} else {
if (mouse && !activeListener && !child._hasMouseEventListener()) { continue; }
// ## ここから
// 対象のシンボルが visible: true、かつ alpha > 0( = オブジェクトが見えている状態で)、かつそのシンボルに hitArea が設定されていた場合 hitArea の nominalBounds の情報を元に hitTest を行う
// nominalBounds は FlashCC が CreateJS ファイルをはき出す際に付加している Rectangle オブジェクト。座標と大きさが入ってる。
if (child.visible && child.alpha > 0 && hitArea && hitArea.nominalBounds) {
// イベントの座標を hitArea のローカル座標に直して、x, y の四辺の座標と比較する
var localRect = hitArea.globalToLocal(x, y);
var nominalBounds = hitArea.nominalBounds;
if ((nominalBounds.x <= localRect.x && localRect.x <= (nominalBounds.x + nominalBounds.width)) &&
(nominalBounds.y <= localRect.y && localRect.y <= (nominalBounds.y + nominalBounds.height))) {
// 配列タイプだったらここで push して返る
if (arr) {
arr.push(child);
continue;
}
// 直接シンボルを返す
else {
return (mouse && !this.mouseChildren) ? this : child;
}
}
// 範囲内に存在しなかったことは確認できてるので、ここで返る
continue;
}
// ## ここまで
child.getConcatenatedMatrix(mtx);
if (hitArea) {
mtx.appendTransform(hitArea.x, hitArea.y, hitArea.scaleX, hitArea.scaleY, hitArea.rotation, hitArea.skewX, hitArea.skewY, hitArea.regX, hitArea.regY);
mtx.alpha = hitArea.alpha;
}
ctx.globalAlpha = mtx.alpha;
ctx.setTransform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx-x, mtx.ty-y);
(hitArea||child).draw(ctx);
if (!this._testHit(ctx)) {
continue;
}
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, 2, 2);
if (arr) {
arr.push(child);
} else {
return (mouse && !this.mouseChildren) ? this : child;
}
}
}
return null;
};
- canvas に転写する手間が省けてるので、処理が軽くなってるとは思われる
- hitArea が何らかの方法で変形されてる(scale がかかってる)とうまくいかないかもしれない
- 矩形の範囲しか指定できない = hitTest が全部矩形で判断されてしまう
- globalToLocal してるので、動いてるオブジェクトに対しても問題なく走る(と思う)
オマケ
上のコードは FlashCC で書きだした前提なので、そのまま生の CreateJS で動いているところには適用できないが
座標値を渡して nominalBounds というプロパティで Rectangle をシンボルに付加すれば動く。
target_symbol.nominalBounds = new createjs.Rectangle(0, 0, 100, 100);