#はじめに
tmlib.jsの製作者である@phiさんにお誘い頂き、Qiitaを始めてみました。
記事を書くのは慣れていないので分かりにくい点が多々あるかもしれませんが、どうぞよろしくお願いします。
tmlib.jsはゲーム制作を目的としたライブラリなので、当然ゲーム制作に最も適しているのですが、
ちょっとしたWebアプリをお手軽に作りたい!という時にも非常に役立ってくれます。
ですが、ドキュメント面がまだまだ乏しく、あまり知られていない機能も数多くあるようです。
今回はv0.3.0の新機能である「クリッピング機能」についてご紹介したいと思います。
#クリッピングとは?
クリッピングとは画像処理で主に使われる用語で、必要な部分だけを切り抜く操作の事です。
親要素の形に合わせて子要素を自動的に切り抜いてくれます。
文章だけでは何のことかさっぱり分かりませんので、さっそくサンプルを見てみましょう。
#サンプル
goo.gl/Vuzs9S
※このサンプルは@phiさんに作成して頂きました
画面上の白い四角部分が親要素で、中の青い四角群が子要素です。
画面をスライドすると青い四角群がスクロールしますが、
白い四角部分からはみ出さないようにクリッピングされています。
サンプルを見てお分かり頂けるかと思いますが、クリッピングとスクロールは非常に相性が良いです。
#解説
// マスク用
this.mask = tm.display.RectangleShape(500, 500, {
fillStyle: "white",
}).addChildTo(this);
this.mask.x = SCREEN_CENTER_X;
this.mask.y = SCREEN_CENTER_Y;
this.mask.width = 500;
this.mask.height = 500;
// こいつを true にすると子供が自分のサイズに合わせてクリッピングされる
this.mask.clipping = true;
各要素はclippingというbool型のプロパティを持っており、trueに設定することで
子要素が親要素からはみ出さないようにクリッピングされます。
非常にシンプルで分かりやすいですね。
clippingをfalseに設定した時の動作も確認してみてください。
#問題点
サンプルを書き換えて、子要素のタッチイベントを追加しましょう。
以下のコードをrect変数に追記します。
rect.checkHierarchy = true;
rect.setInteractive(true);
rect.setBoundingType('rect');
rect.on('pointingstart', function(e){
console.log('touch ' + i);
});
(実行結果はこちら)
青い四角形をタッチすると、コンソール画面に「touch x(xは数字)」と表示されるようになったかと思います。
ここで白い四角形の外の部分、本来10番目の青い四角形がある辺りをタッチしてみてください。
そうすると困ったことが起きてしまいます。
そうなんです。子要素はクリッピングされて一見存在しないように見えるのですが、
タッチ判定はしっかり残ってしまっているのです。
このままではユーザーの意図しない誤操作を招く原因にもなりかねません。
#解決策
解決策は非常に簡単で、少し手直ししてやるだけです。
要はタッチ箇所が親要素の外の時は処理を行なわないようにすれば良いのです。
変数rectのタッチイベントの処理を以下のように書き換えます。
rect.on('pointingstart', function(e){
var p = e.app.pointing;
//マスクの外では処理を行なわない
if(!this.mask.isHitPointRect(p.x, p.y)) {
return;
}
console.log('touch ' + i);
}.bind(this));
(実行結果はこちら)
これでタッチイベントを持つ子要素も正常にクリッピング出来るようになりました。
当たり判定の計算は円形か長方形のみなので、複雑な形のマスクには対応できないかもしれませんが・・・
#おまけ
@phiさんよりスクロール操作時にタッチイベントが発生しないように修正を加えたサンプルを頂きました!
少々記述は増えたものの、比較的シンプルな処理となっています。
var sx;
var sy;
rect.on('pointingstart', function(e){
var p = e.app.pointing;
sx = p.x; sy = p.y;
}.bind(this));
rect.on('pointingend', function(e) {
var p = e.app.pointing;
//マスクの外では処理を行なわない
if(!this.mask.isHitPointRect(p.x, p.y)) {
return ;
}
// スクロール時も処理を行わない
var ex = p.x;
var ey = p.y;
var len = (ex-sx)*(ex-sx)+(ey-sy)*(ey-sy);
if (len > 16) return ;
console.log('touch ' + i);
}.bind(this));
(実行結果はこちら)
タッチ開始時と終了時の座標からユークリッド距離(二点間の距離)を求めています。
距離が規定値を超えたらスクロール操作と見なし、タッチイベントを発生させないという処理ですね。
#おわりに
今回初めてQiitaを使用してみましたが、編集が非常に容易で使いやすく感じました。
実行結果の表示には@phiさん製作のrunstantを使用しました。
ソースと動作を即座に確認することが出来る上、記事に載せるコードも必要最小限で済むので大変便利です。
みんなでrunstantを使おう!