EaselJSのMouseEventを理解しよう

EaselJSには、MouseEventを処理するonClick, onDoubleClick, onMouseOver, onMouseOut, onPress, onMouseMove, onMouseUpといったイベントハンドラが用意されています。記述に関してはActionScript2.0に似ていますが、デフォルトでonMouseOver, onMouseOutが使用できない点やonMouseMove, onMouseUpの扱いなど、若干注意すべき点があります。

追記 2013/12/18

EaselJSは、ver 0.6でイベントリスナーの仕組みが導入され、ver 0.7ではイベントハンドラが削除されました。「新しくなったCreateJSのイベントリスナーを理解しよう」や「EaselJS ver 0.7で変更されたイベント処理を理解しよう」で解説していますので、よろしければ合わせてご覧下さい。

スマートフォンやタブレットのタッチイベントに対応させる

まず、スマートフォンやタブレットのタッチイベントに対応させる方法を見ておきましょう。EaselJSには、タッチイベントを処理するTouchクラスが用意されており、簡単な記述だけでタッチイベントをマウスイベントと同様に扱うことができます。

Touch.isSupported()は、ブラウザがタッチイベントをサポートしているかをBool値で返します。タッチイベントをサポートした環境では、Touch.enable()でStageインスタンスの参照を渡すことにより、タッチイベントをonClick, onMouseOver, onMouseOut, onPress, onMouseMove, onMouseUpといったイベントハンドラで処理できるようになります。

JavaScript

if (Touch.isSupported()) {
  Touch.enable(_stage);
}

onClickイベントハンドラ

onClickイベントハンドラは、オブジェクト上でマウスがクリックされると呼び出され、引数でMouseEventオブジェクトを受け取ります。MouseEventオブジェクトは、typeにマウスイベントの種類、nativeEventにブラウザのネイティブMouseEventオブジェクト、stageXとstageYにイベントが発生したstage上の座標、targetにイベントハンドラが登録されたオブジェクトを持っています。また、イベントハンドラ内のthisは、targetと同じくイベントが登録されたオブジェクトとなります。

下記の例では、stage中央の円形のShapeインスタンスにonClickイベントハンドラを登録し、クリックするとShapeインスタンスの色が変わるようにしています。また、JavaScriptコンソールでMouseEventオブジェクトのプロパティが出力されます。

JavaScript

_circle.onClick = function(e) {
  console.log("e.type :", e.type);
  console.log("e.nativeEvent :", e.nativeEvent);
  console.log("e.stageX :", e.stageX);
  console.log("e.stageY :", e.stageY);
  console.log("e.target :", e.target);
  console.log("this === e.target :", this === e.target);
  var g = e.target.graphics;
  g.clear();
  g.beginFill(Graphics.getHSL((Math.random() * 360) >> 0, 80, 50));
  g.drawCircle(0, 0, 50);
  g.endFill();
};

onDoubleClickイベントハンドラ

onDoubleClickイベントハンドラは、オブジェクト上でマウスがダブルクリックされると呼び出され、onClickと同様に引数でMouseEventオブジェクトを受け取ります。下記の例では、円形のShapeインスタンスがダブルクリックされた際にトゥイーンを実行するようにしています。

JavaScript

_circle.onDoubleClick = function(e) {
  Tween.get(e.target, {override:true})
  .to({scaleX:0.5, scaleY:0.5})
  .to({scaleX:1, scaleY:1}, 1000, Ease.elasticOut);
};

onMouseOverとonMouseOutを有効にするenableMouseOver()

onMouseOverとonMouseOutを使用するには、まずStageインスタンスのenableMouseOver()をコールする必要があります。enableMouseOver()の引数で、マウスとオブジェクトの衝突判定を1秒間に何回行うかを指定します。引数を省略した場合は20、指定できる最大値は50となります。また、0を指定した場合は、onMouseOverとonMouseOutは使用できなくなります。

下記の例では、enableMouseOver(30)としているため、1秒間に30回、マウスとShapeインスタンスの衝突判定が行われます。onMouseOverイベントハンドラでは、ColorFilterを使ってShapeインスタンスの色を変更しています。

JavaScript

_stage.enableMouseOver(30);
_circle.onMouseOver = function(e) {
  this.filters = [new ColorFilter(0.85, 0.85, 0.85, 1)];
  this.updateCache();
};
_circle.onMouseOut = function(e) {
  this.filters = null;
  this.updateCache();
};

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

クリックできるオブジェクトは、マウスオーバーした際にマウスカーソルをハンドカーソルに変更した方がユーザーに親切です。しかし、リファレンスを見る限り、現在のCreateJSではFlashのbuttonModeのようなプロパティは用意されていないようです。

そこで、下記の例ではマウスオーバー時にcanvas要素自体のスタイルシートを変更することで、ハンドカーソルに変更しています。onMouseOverでcanvas.style.cursorに"pointer"を指定し、onMouseOutで"default"に戻しています。

JavaScript

_circle.onMouseOver = function(e) {
  _stage.canvas.style.cursor = "pointer";
  this.filters = [new ColorFilter(0.85, 0.85, 0.85, 1)];
  this.updateCache();
};
_circle.onMouseOut = function(e) {
  _stage.canvas.style.cursor = "default";
  this.filters = null;
  this.updateCache();
};
_circle.onClick = function(e) {
  Tween.get(this, {override:true})
  .to({scaleX:0.5, scaleY:0.5})
  .to({scaleX:1, scaleY:1}, 1000, Ease.elasticOut);
};

onPress, onMouseMove, onMouseUpを使ったドラッグ&ドロップ

EaselJSでドラッグ&ドロップを実装するには、onPress, onMouseMove, onMouseUpイベントハンドラを使用します。まず、対象となるオブジェクトにonPressイベントハンドラを設定します。onPressイベントハンドラは、オブジェクト上でマウスボタンが押された際に呼ばれ、引数でMouseEventオブジェクトを受け取ります。

次にonPressで受け取ったMouseEventオブジェクトに対して、onMouseMoveとonMouseUpのイベントハンドラを設定し、マウスの移動とドラッグの終了を検知します。 onMouseMoveとonMouseUpは、ドラッグする表示オブジェクトではなく、onPressで受け取ったMouseEventオブジェクトに設定する点に注意しましょう。

また、onMouseMoveとonMouseUpでは、イベントハンドラ内のthis参照にも注意する必要があります。下記は、円形のShapeインスタンスをドラッグ&ドロップする例です。onPressイベントハンドラ内のthisは、onPressで受け取ったMouseEventのtargetプロパティ(イベントハンドラが登録されたShapeインスタンス)となりますが、onMouseMoveとonMouseUpイベントハンドラ内のthisは、onPressで受け取ったMouseEventとなります。

JavaScript

_circle.onPress = function(pressEvent) {
  console.log("onPress ", "this === pressEvent.target : ", this === pressEvent.target);
  var offsetX = this.x - pressEvent.stageX;
  var offsetY = this.y - pressEvent.stageY;
  pressEvent.onMouseMove = function(mouseMoveEvent) {
    console.log("onMouseMove ", "this === pressEvent : ", this === pressEvent);
    this.target.x = mouseMoveEvent.stageX + offsetX;
    this.target.y = mouseMoveEvent.stageY + offsetY;
  };
  pressEvent.onMouseUp = function(mouseUpEvent) {
    console.log("onMouseUp ", "this === pressEvent : ", this === pressEvent);
    delete this.onMouseMove;
    delete this.onMouseUp;
  };
};

単独でMouseMoveイベントを使う

前述したように現在のEaselJSでは、onMouseMoveはonPressで受け取ったMouseEventオブジェクトにしか設定できないため、単独で使用することができません。しかし、JavaScriptネイティブのmousemoveイベントをcanvas要素に登録することで、単独でも使用することができます。

下記の例では、canvas要素に対してmousemoveイベントのリスナーを追加しています。引数で受け取るMouseEventオブジェクトのpageX, pageYプロパティは、ページ左上からの座標なので、この値からcanvas要素の座標を引くことでStage上の座標に変換することができます。サンプルでは、矢印のShapeインスタンスをマウス座標に向けるようにしています。

JavaScript

function eventInit() {
  if (!Touch.isSupported()) {
    _canvas.addEventListener("mousemove", mouseMoveHandler, false);
  } else {
    _canvas.addEventListener("touchstart", touchHandler, false);
    _canvas.addEventListener("touchmove", touchHandler, false);
    _canvas.addEventListener("touchend", touchHandler, false);
  }
  Ticker.addListener(_instance);
}

function mouseMoveHandler(e) {
  _mouseX = e.pageX - e.target.offsetLeft;
  _mouseY = e.pageY - e.target.offsetTop;
}

function touchHandler(e) {
  e.preventDefault();
  switch (e.type) {
    case "touchstart" :
    case "touchmove" :
      var touch = e.touches[0];
      var target = touch.target;
      _mouseX = touch.pageX - target.offsetLeft;
      _mouseY = touch.pageY - target.offsetTop;
      break;
    case "touchend" :
      break;
  }
}