本稿は、2013年12月21日土曜日に催された第11回「Creators MeetUp」で務めた一席「新しいCreateJSのマウスイベント」のまとめとして書いた。サンプルコードはjsdo.itに掲げている。また、当日の映像はUSTREAM録画として公開された。
Video streaming by Ustream
CreateJSのライブラリは2013年9月にアップグレードされ、12月17日付CreateJS Blogで+0.0.1のマイナーアップデートがリリースされた(図001)。とくに大きな変更が加えられたのはEaselJSのイベントモデルだ。EaselJS 0.7.0と0.7.1を併せて、とくにマウスイベントの扱いについて説明する(詳しくは、「EaselJS 0.7.0の改訂」および「EaselJS 0.7.1が公開される」参照)。
図001■2013年12月にリリースされたCreateJSの最新版ライブラリ
01 ドラッグ&ドロップ
ドラッグ&ドロップでは、3つのマウス操作についてマウスイベントの扱いを考える。
ドラッグ&ドロップで扱うマウスイベント
- インスタンスの上でマウスボタンが押されたらドラッグを始める
- ボタンを押したままマウスを動かしたらインスタンスが追いかける
- マウスボタンを放したらドラッグは終える
EaselJS 0.7.0では、マウスイベントのリスナーを加えるインスタンスはDisplayObjectにまとめられた。リスナーを除くために、イベントオブジェクトの参照を保たなくて済む。ただし、EaselJS 0.6.1からイベント名が変わったことに注意しなければならない(表001)。
表001■EaselJS 0.7.0からドラッグ&ドロップに用いるマウスイベント
MouseEventクラスの
イベント(0.6.1) |
DisplayObjectクラスの
イベント(0.7.0) |
マウス操作 |
|
mousedown
|
インスタンス上でマウスボタンが押された。
|
mousemove → |
pressmove
|
インスタンス上でマウスボタンが押されたまま、ポインタが動かされた。
|
mouseup → |
pressup
|
インスタンス上で押されたマウスボタンが放された。
|
コード001■EaselJS 0.7.1でインスタンスをドラッグ&ドロップする
- instance.addEventListener("mousedown", startDrag);
- function startDrag(eventObject) {
- var instance = eventObject.target;
- instance.addEventListener("pressmove", drag);
- instance.addEventListener("pressup", stopDrag);
- }
- function drag(eventObject) {
- var instance = eventObject.target;
- instance.x = eventObject.stageX;
- instance.y = eventObject.stageY;
- stage.update();
- }
- function stopDrag(eventObject) {
- var instance = eventObject.target;
- instance.removeEventListener("pressmove", drag);
- instance.removeEventListener("pressup", stopDrag);
- }
|
サンプル001■EaselJS 0.7.1でShapeインスタンスをドラッグ&ドロップする
02 マウスイベントのバブリングとその停止
バブリングというのは、泡のように上っていくこと。EaselJS 0.7.0から、ほとんどのマウスイベントは、イベントが起こったインスタンス(ターゲット)から表示リストの階層を上って伝わるようになった。その流れに先立って、表示リストの頂点からターゲットに向かって伝わることをキャプチャという(図002)。
図002■イベントのバブリングとキャプチャ
サンプルとして後にコード002を掲げた。ボタンにするShape(button)と背景画像のBitmapインスタンスを、Containerオブジェクト(container)に入れ子にした。そして、ボタンをクリックするとURLが開き、親Containerオブジェクトは画像とボタンをまとめてドラッグできるようにしたい(図003)。
図003■ボタンのクリックと背景画像のドラッグ
ボタンはクリック
|
画像とボタンは親がまとめてドラッグ
|
ドラッグのスクリプトは、基本的に前掲コード001と変わらない。ただし、ドラッグするのは、ふたつのインスタンスを入れ子にした親Containerオブジェクト(container)だ。そして、リスナーが加えられてイベントを処理しているインスタンスはEvent.currentTargetプロパティで参照する(第6、第13、および第19行目)。
ボタンのShapeインスタンス(button)については、DisplayObject.clickイベントに加えたリスナー関数(navigateToURL())でURLを開けばよい(第3および第23〜25行目)。
- container.addEventListener("mousedown", startDrag);
- button.addEventListener("click", navigateToURL);
- function startDrag(eventObject) {
- var instance = eventObject.currentTarget;
- instance.addEventListener("pressmove", drag);
- instance.addEventListener("pressup", stopDrag);
- }
- function drag(eventObject) {
- var instance = eventObject.currentTarget;
- instance.x = eventObject.stageX;
- instance.y = eventObject.stageY;
- stage.update();
- }
- function stopDrag(eventObject) {
- var instance = eventObject.currentTarget;
- instance.removeEventListener("pressmove", drag);
- instance.removeEventListener("pressup", stopDrag);
- }
- function navigateToURL(eventObject) {
- window.open("http://plus.adobe-adc.jp/2_10/", "_blank");
- }
|
ただし、このスクリプトだけでは、Shapeインスタンスの上でマウスボタンを押すと、イベントはバブリングして親Containerオブジェクト(container)に伝わる。すると、リスナー関数(startDrag())が呼出され、ドラッグを始めたことになってしまう。
そこで、マウスイベントのバブリングを止めよう。ボタンのShapeインスタンス(button)にも、DisplayObject.mousedownイベントのリスナー(stopEvent())を加える(第4行目)。そして、リスナー関数は、Event.stopPropagation()メソッドで親オブジェクトへのイベントを止める(第26〜28行目)。
Eventオブジェクト.stopPropagation()
コード002■EaselJS 0.7.1で入れ子インスタンスのマウスイベントがバブリングするのを止める
- var offset = new createjs.Point();
- container.addEventListener("mousedown", startDrag);
- button.addEventListener("click", navigateToURL);
- button.addEventListener("mousedown", stopEvent);
- function startDrag(eventObject) {
- var instance = eventObject.currentTarget;
- offset.x = instance.x - eventObject.stageX;
- offset.y = instance.y - eventObject.stageY;
- instance.addEventListener("pressmove", drag);
- instance.addEventListener("pressup", stopDrag);
- }
- function drag(eventObject) {
- var instance = eventObject.currentTarget;
- instance.x = eventObject.stageX + offset.x;
- instance.y = eventObject.stageY + offset.y;
- stage.update();
- }
- function stopDrag(eventObject) {
- var instance = eventObject.currentTarget;
- instance.removeEventListener("pressmove", drag);
- instance.removeEventListener("pressup", stopDrag);
- }
- function navigateToURL(eventObject) {
- window.open("http://plus.adobe-adc.jp/2_10/", "_blank");
- }
- function stopEvent(eventObject) {
- eventObject.stopPropagation();
- }
|
サンプル002■EaselJS 0.7.1で入れ子インスタンスのマウスイベントのバブリングを止める
03 マスクをドラッグする
EaselJSでは、マスクをドラッグすることもできる。ただし、バブリングするマウスイベントには気をつけなければならない。サンプル003はEaselJS 0.6.1でつくられているため、マウスイベントはバブリングしない。
サンプル003■EaselJS 0.6.1でインスタンスにフィルタとマスクをかける
同じつくり方でEaselJS 0.7.0を用いると、マスクがドラッグできなくなる。手前のBitmapインスタンスがマウスイベントを奪ってしまうためだ。それを防ぐには、手前に重なるインスタンスすべてのDisplayObject.mouseEnabledプロパティをfalseに定めなければならない。
サンプル004■EaselJS 0.7.0でインスタンスにフィルタとマスクをかける
04 EaselJS 0.7.1のマウスイベントのバブリング
EaselJS 0.7.1では、インスタンスのイベントのフロー(バブリング)の中にマウスイベントのリスナーをもつインスタンスがないかぎり、マウスイベントは奪わない。
サンプル005■EaselJS 0.7.1でインスタンスにフィルタとマスクをかける
だが、ひとたび上司(親インスタンス)がリスナーをもつと、部下(子インスタンス)にはリスナーがなくても、報告責任(マウスイベント)が生じてバブリングする。つまり、「部下のイベントは上司のモノ」。その裏返しとして、背面のインスタンスはイベントを横取りされる。悔しかったら「倍返しだ!」。
図004■親インスタンスがリスナーをもつと子インスタンスのマウスイベントはバブリングする
コード003■EaselJS 0.7.1で親インスタンスがリスナーをもつと入れ子のマウスイベントはバブリングする
- stage.addChild(container);
- container.addChild(instance);
- container.addChild(no_listener);
- container.addEventListener("mousedown", function() {});
- instance.addEventListener("click", rotate);
- function rotate(eventObject) {
- var instance = eventObject.currentTarget;
- instance.rotation += 30;
- stage.update();
- }
|
作成者: 野中文雄
更新日: 2014年1月10日 USTREAM動画を追加。
作成日: 2013年12月21日