新しくなったCreateJSのイベントリスナーを理解しよう

少し遅くなりましたが、先日CreateJSの新バージョンが公開され、EaselJSのバージョンは0.6.0となりました。新バージョンのEaselJSには様々な新機能が追加されていますが、その中で最も大きな変更点は、EventDispatcherクラスの追加と共にイベントリスナーの仕組みが導入された点でしょう。今回は、いくつかのサンプルを通してEaselJSのイベントリスナーの使い方をご紹介したいと思います。

追記 2013/12/18

EaselJS ver 0.7では、イベント関連の仕様が若干変更されました。「EaselJS ver 0.7で変更されたイベント処理を理解しよう」で解説していますので、よろしければ合わせてご覧下さい。

イベントリスナーを追加する addEventListener()

オブジェクトにイベントリスナーを追加するには、addEventListener()を使用します。第1引数でイベントの種類を文字列で指定し、第2引数でリスナーとなる関数を指定します。下記のサンプルでは、ステージ中央に配置した円形のオブジェクト_circleにclick時のリスナー関数としてclickHandlerを追加しています。結果として、_circleがクリックされるとclickHandlerが実行されます。

リスナー関数内のthis参照は、Windowオブジェクトになります。イベントが発生したオブジェクトは、引数で受け取ったイベントオブジェクトのtargetプロパティから取得できます。

また、addEventListener()の第2引数には、handleEvent()メソッドを実装したオブジェクトを指定することもできます。例として、StageクラスはhandleEvent()メソッドを実装しているため、TickerクラスのtickイベントのリスナーとしてStageインスタンスを渡すことができます。

JavaScript

function init(canvasID) {
  _canvas = document.getElementById(canvasID);
  _stage = new createjs.Stage(_canvas);
  if (createjs.Touch.isSupported()) {
    createjs.Touch.enable(_stage);
  }
  _circle = createCircle();
  _circle.addEventListener("click", clickHandler);
  createjs.Ticker.setFPS(30);
  createjs.Ticker.useRAF = true;
  createjs.Ticker.addEventListener("tick", _stage);
}

function clickHandler(evt) {
  console.log(this); // 出力:Window
  var target = evt.target;
  console.log(target.name); // 出力:circle
  scaleTween(target);
}

イベントリスナーを削除する removeEventListener()

オブジェクトからイベントリスナーを削除するには、removeEventListener()を使用します。引数はaddEventListener()と同様です。下記のサンプルでは、クリック時に実行されるリスナー関数clickHandler()内でclickのイベントリスナーを削除しています。結果として、clickHandler()は1回目のクリック時にしか実行されません。

また、オブジェクトがイベントリスナーを保持しているかは、hasEventListener()で調べることができます。hasEventListener()は、引数で指定したイベントのリスナーを保持していればtrue、保持していなければfalseを返します。

JavaScript

function clickHandler(evt) {
  var target = evt.target;
  console.log(target.hasEventListener("click")); // 出力:true
  target.removeEventListener("click", clickHandler);
  console.log(target.hasEventListener("click")); // 出力:false
  scaleTween(target);
}

イベントリスナーとイベントハンドラの違い

イベントリスナーはイベントハンドラと比べてどのような利点があるのでしょうか?簡単なサンプルを通して確認してみましょう。下記のサンプルでは、左側に配置した_circleLにはaddEventListener()で、右側に配置した_circleRにはonClickイベントハンドラでそれぞれ2つの関数を実行するようにしています。

クリックしてみるとイベントリスナーでは2つの関数が実行されるのに対して、onClickイベントハンドラでは最後に代入した関数しか実行されないことがわかります。このようにイベントリスナーでは、同じイベントに対して複数の関数を登録できるメリットがあります。

イベントハンドラは現在のEaselJS 0.6.0ではまだ使用できますが、将来的に廃止予定とされているため、早めにイベントリスナーに慣れておきましょう。

JavaScript

_circleL = createCircle("circleL", centerX - 100);
_circleL.addEventListener("click", changeColor);
_circleL.addEventListener("click", scaleTween);
_circleR = createCircle("circleR", centerX + 100);
_circleR.onClick = changeColor;
_circleR.onClick = scaleTween;

マウスオーバー時にカーソルをハンドカーソルに変更する

EaselJS 0.6.0では、マウスオーバー時にマウスカーソルを変更するcursorプロパティが追加されました。ActionScript3.0のbuttonModeのようなプロパティです。マウスオーバー時にカーソルをハンドカーソルに変更するには、DisplayObjectインスタンスのcursorプロパティに"pointer"を指定します。

JavaScript

_stage.enableMouseOver(30);
_circle.addEventListener("mouseover", mouseOverHandler);
_circle.addEventListener("mouseout", mouseOutHandler);
_circle.addEventListener("click", clickHandler);
_circle.cursor = "pointer";

イベントリスナーを使ったドラッグ&ドロップ

過去の投稿「EaselJSのMouseEventを理解しよう」では、イベントハンドラを使ったドラッグ&ドロップの例を紹介しましたが、イベントリスナーを使ったドラッグ&ドロップの例を見てみましょう。

EaselJSでは、"mousemove", "mouseup"イベントはMouseEventオブジェクトでしか扱うことができません。そのため、"mousedown"イベントのリスナー関数で受け取ったMouseEventオブジェクトに対してaddEventListener()で"mousemove"と"mouseup"のリスナーを追加します。

前述したようにリスナー関数内のthis参照がWindowオブジェクトになる点には注意が必要です。下記のサンプルでは、mouseDownHandler()内にmouseMoveHandler()とmouseUpHandler()を定義しています。このようにクロージャを使うことにより、mouseMoveHandler()やmouseUpHandler()内でもmouseDownHandler()で宣言したtarget, offsetX, offsetYにアクセスできます。

JavaScript

function mouseDownHandler(mouseDownEvent) {
  _isDragging = true;
  var target = mouseDownEvent.target;
  var offsetX = target.x - mouseDownEvent.stageX
  var offsetY = target.y - mouseDownEvent.stageY;
  _lastPoint = new createjs.Point(target.x, target.y);
  function mouseMoveHandler(mouseMoveEvent) {
    _lastPoint = new createjs.Point(target.x, target.y);
    target.x = mouseMoveEvent.stageX + offsetX;
    if (areaLimiter("x", target)) {
      offsetX = target.x - mouseMoveEvent.stageX;
    }
    target.y = mouseMoveEvent.stageY + offsetY;
    if (areaLimiter("y", target)) {
      offsetY = target.y - mouseMoveEvent.stageY;
    }
  }
  function mouseUpHandler(mouseUpEvent) {
    mouseDownEvent.removeEventListener("mousemove", mouseMoveHandler);
    mouseDownEvent.removeEventListener("mouseup", mouseUpHandler);
    _inertiaForce = new createjs.Point(target.x, target.y).subtract(_lastPoint);
    _lastPoint = null;
    _isDragging = false;
  }
  mouseDownEvent.addEventListener("mousemove", mouseMoveHandler);
  mouseDownEvent.addEventListener("mouseup", mouseUpHandler);
}

イベントを発生させる dispatchEvent()

dispatchEvent()を使うと任意のタイミングでイベントを発生させることができます。下記のサンプルでは、ステージ中央に配置したオブジェクトにmousedownするとchangeColor()が実行され、そのままmouseupするとscaleTween()が実行されます。また、mousedownしたままmouseoutした際にもdispatchEvent()でmouseupイベントを発生させているため、scaleTween()が実行されます。

JavaScript

function mouseDownHandler(mouseDownEvent) {
  var target = mouseDownEvent.target;
  changeColor(target);
  function mouseUpHandler(mouseUpEvent) {
    mouseDownEvent.removeEventListener("mouseup", mouseUpHandler);
    target.removeEventListener("mouseout", mouseOutHandler);
    scaleTween(target);
  }
  function mouseOutHandler(mouseOutEvent) {
    var mde = mouseDownEvent;
    var moe = mouseOutEvent;
    var mue = new createjs.MouseEvent("mouseup", moe.stageX, moe.stageY, mde, null, moe.pointerID, mde.primary, moe.rawX, moe.rawY);
    mouseDownEvent.dispatchEvent(mue);
  }
  mouseDownEvent.addEventListener("mouseup", mouseUpHandler);
  target.addEventListener("mouseout", mouseOutHandler);
}

Stageクラスに追加されたマウスイベント

EaselJS 0.6.0では、Stageクラスで扱えるマウスイベントとして"stagemousemove", "stagemousedown", "stagemouseup"が追加されました。前述したようにDisplayObjectに定義された"mousemove"や"mouseup"は単独で使用することはできませんが、"stagemousemove"と"stagemouseup"は単独でも使用することができます。

下記のサンプルでは、"stagemousemove"の処理で矢印のShapeインスタンスをマウス座標に向けるようにしています。また、"stagemousedown"で矢印のShapeインスタンスを縮小し、"stagemouseup"で元の大きさに戻しています。

JavaScript

_stage.addEventListener("stagemousemove", stageMouseMoveHandler);
_stage.addEventListener("stagemousedown", stageMouseDownHandler);
_stage.addEventListener("stagemouseup", stageMouseUpHandler);
createjs.Ticker.addEventListener("tick", _instance.tick);