サイトトップ

Director Flash 書籍 業務内容 プロフィール

HTML5テクニカルノート

EaselJSのつぎのバージョンにEventDispatcherクラスが加わる

ID: FN1212001 Technique: HTML5 and JavaScript

本稿改訂時、CreateJSサイトにはつぎのバージョンのリリース候補が公開されています。EaselJSライブラリには新しいイベントモデルが備わるようです(「EventDispatcher」参照)。新たにEventDispatcherクラスが加えられ、DisplayObjectインスタンスからEventDispatcher.addEventListener()メソッドを呼出してイベントリスナーが登録できるようになります。


01 イベントリスナーによるイベントの捉え方

イベントリスナーも、イベントハンドラと同じように、イベントが起こったときに予め定めた関数を呼出す仕組みです。イベントハンドラは、インスタンスのイベントにハンドラとなるコールバック関数を定めます。それに対して、イベントリスナーはインスタンスへのEventDispatcher.addEventListener()メソッドの呼出しにより、リスナーとなる関数を登録します。

【イベントハンドラ】
DisplayObjectインスタンス.イベント = ハンドラ

【イベントリスナー】
DisplayObjectインスタンス.addEventListener(イベント名, リスナー)

リスナー関数は、イベントハンドラと同じように、引数にイベントオブジェクトを受取ります。

function リスナー関数(イベントオブジェクト) {
  // 処理内容
}

たとえば、インスタンス(instance)をクリックしたとき呼出すイベントリスナー(clickHandler)は、EventDispatcher.addEventListener()メソッドをつぎのように呼出して登録します。クリックイベントは文字列"click"です。

instance.addEventListener("click", clickHandler);
function clickHandler(eventObject) {
  // 処理内容
}

イベントハンドラからコールバック関数を除くには、イベントにnullを代入しました。

【イベントハンドラ】
DisplayObjectインスタンス.イベント = null

イベントリスナーの呼出しが要らなくなったときには、EventDispatcher.removeEventListener()メソッドの呼出しにより、インスタンスからリスナー関数を取除きます。渡すふたつの引数は、イベントリスナーを登録したEventDispatcher.addEventListener()メソッドと同じです。

【イベントリスナー】
DisplayObjectインスタンス.removeEventListener(イベント名, リスナー)

02 イベントハンドラをイベントリスナーに書替える

EaselJSのマウスクリックとドラッグ&ドロップ」の解説では、イベントハンドラでマウスイベントを扱いました。このコード003「円のShapeインスタンスをドラッグする」(図001)を、イベントリスナーによる処理に書替えてみましょう。

図001■クリックしたマウスポインタの位置を保ちつつインスタンスがドラッグできる
図001

イベントリスナーを使って書き直したscript要素全体は、コード001として後に掲げます。その中でイベントハンドラから書替えているステートメントを抜出してご説明します(行番号はコード001より)。まず、新しいEventDispatcherクラスをscript要素に読込みます(第2行目)。

  1. <script src="easeljs/events/EventDispatcher.js"></script>

つぎに、ドラッグの始まりとなるマウスボタンを押すイベントは"mousedown"です。DisplayObject.onPressイベントに定めていたハンドラのコールバック関数(pressHandler())は、EventDispatcher.addEventListener()メソッドでリスナーに置換えます(第20行目)。

  1. function xInitialize() {
  1.     var myInstance = xDrawCircle(20);
        // myInstance.onPress = pressHandler;
  1.     myInstance.addEventListener("mousedown", pressHandler);
  1. }

そして、"mousedown"イベントのリスナー関数(pressHandler())では、さらにマウスを動かす"mousemove"イベントに、ドラッグのふるまいを定めたリスナー(mouseMoveHandler()が加わります(第37行目)。

リスナー関数(pressHandler())が担うのは、インスタンスをドラッグするための準備です。このときthis参照は、これまでのイベントハンドラ(DisplayObject.onPressイベント)では、クリックしたオブジェクトでした。これが、イベントリスナーでは、グローバルオブジェクト(ブラウザであればWindowオブジェクト)になります。そのため、ドラッグするインスタンスは、thisキーワードでなくMouseEvent.targetプロパティで参照を得ます(第41行目)。

  1. function pressHandler(eventObject) {
      // eventObject.instance = instance;
      // eventObject.onMouseMove = mouseMoveHandler
    ;
  1.   eventObject.addEventListener("mousemove", mouseMoveHandler);
  1. }
  2. function mouseMoveHandler(eventObject) {
      // var target = this.instance;
  3.   var target = eventObject.target;
  1. }

マウスボタンを放す"mouseup"イベントのリスナーには、ドラッグが済んだ後処理(mouseUpHandler())を定めます(第47〜51行目)。ドラッグし終わると、"mousemove"と"mouseup"イベントのリスナーは、もはや要らなくなります。そこで、それらのリスナー関数はEventDispatcher.removeEventListener()メソッドで除きました(第49〜50行目)。

  1. function pressHandler(eventObject) {
  2.   var instance = eventObject.target;
  1.   instance.dispatcher = eventObject;
      // eventObject.onMouseUp = mouseUpHandler;
  1.   eventObject.addEventListener("mouseup", mouseUpHandler);
  2. }
  1. function mouseUpHandler(eventObject) {
  2.   var dispatcher = eventObject.target.dispatcher;
      // this.onMouseMove = this.onMouseUp = null;
  3.   dispatcher.removeEventListener("mousemove", mouseMoveHandler);
  4.   dispatcher.removeEventListener("mouseup", mouseUpHandler);
  5. }

EventDispatcher.removeEventListener()メソッドを呼出すときは、リスナーが登録されているオブジェクトを参照します。"mousemove"と"mouseup"イベントのリスナーは、マウスボタンを押したリスナー関数(pressHandler())が受取ったMouseEventオブジェクトに加えました。つまり、このオブジェクトをマウスボタンを放すリスナー関数(mouseUpHandler())から参照しなければなりません[*1]

そのため、マウスボタンを押したリスナー関数(pressHandler())は、引数のMouseEventオブジェクト(eventObject)をドラッグするオブジェクト(instance)の変数(dispatcher)に定めました(第36行目)。そして、マウスボタンを押したリスナー関数(pressHandler())は、引数に受取ったMouseEventオブジェクト(eventObject)のMouseEvent.targetプロパティでドラッグするオブジェクトの参照が得られます(第48行目)。そこで、その変数(dispatcher)からリスナー関数が登録されたオブジェクトを取出し、EventDispatcher.removeEventListener()メソッドでリスナーを除きました(第49〜50行目)。

これで、イベントハンドラからイベントリスナーの処理に書替えられました。script要素全体はつぎのコード001のとおりです。なお、最新バージョンに加えられた名前空間createjsを、クラスの参照に添えています(名前空間については「CreateJS Suiteのクラスに名前空間が設定される」参照)。

コード001■円のShapeインスタンスをドラッグ&ドロップする
  1. <script src="easeljs/utils/UID.js"></script>
  2. <script src="easeljs/events/EventDispatcher.js"></script>
  3. <script src="easeljs/events/MouseEvent.js"></script>
  4. <script src="easeljs/geom/Matrix2D.js"></script>
  5. <script src="easeljs/geom/Point.js"></script>
  6. <script src="easeljs/display/DisplayObject.js"></script>
  7. <script src="easeljs/display/Container.js"></script>
  8. <script src="easeljs/display/Stage.js"></script>
  9. <script src="easeljs/display/Graphics.js"></script>
  10. <script src="easeljs/display/Shape.js"></script>
  11. <script type="text/javascript">
  12. var stage;
  13. function xInitialize() {
  14.   var canvasObject = document.getElementById("myCanvas");
  15.   stage = new createjs.Stage(canvasObject);
  16.   for (var i = 0; i < 2; i++) {
  17.     var myInstance = xDrawCircle(20);
  18.     myInstance.x = 50 * (i + 1);
  19.     myInstance.y = 50;
  20.     myInstance.addEventListener("mousedown", pressHandler);
  21.   }
  22.   stage.update();
  23. }
  24. function xDrawCircle(nRadius) {
  25.   var myShape = new createjs.Shape();
  26.   var myGraphics = myShape.graphics;
  27.   stage.addChild(myShape);
  28.   myGraphics.beginStroke("#0000FF");
  29.   myGraphics.beginFill("#00FFFF");
  30.   myGraphics.drawCircle(0, 0, nRadius);
  31.   return myShape;
  32. }
  33. function pressHandler(eventObject) {
  34.   var instance = eventObject.target;
  35.   instance.offset =
      new createjs.Point(instance.x - eventObject.stageX, instance.y - eventObject.stageY);
  36.   instance.dispatcher = eventObject;
  37.   eventObject.addEventListener("mousemove", mouseMoveHandler);
  38.   eventObject.addEventListener("mouseup", mouseUpHandler);
  39. }
  40. function mouseMoveHandler(eventObject) {
  41.   var target = eventObject.target;
  42.   var offset = target.offset;
  43.   target.x = eventObject.stageX + offset.x;
  44.   target.y = eventObject.stageY + offset.y;
  45.   stage.update();
  46. }
  47. function mouseUpHandler(eventObject) {
  48.   var dispatcher = eventObject.target.dispatcher;
  49.   dispatcher.removeEventListener("mousemove", mouseMoveHandler);
  50.   dispatcher.removeEventListener("mouseup", mouseUpHandler);
  51. }
  52. </script>

[*1] Function.bind()メソッドを用いれば、リスナー関数のthis参照を自由に変えることができます。メソッドの引数に渡したオブジェクトが、呼出した関数のthis参照に定められ、その関数(Functionオブジェクト)を返します。

関数.bind(this参照)

したがって、Function.bind()メソッドで、マウスボタンを放すリスナー関数(mouseUpHandler())のthis参照を変える手も考えられます。ただし、このメソッドの使い方には注意が必要なうえ、本稿の例ではあまり利点もありません。詳しくは、「EaselJSのイベントリスナーにFunction.bind()メソッドを適用するとリスナー内から削除できない」をお読みください。


作成者: 野中文雄
更新日: 2013年2月7日 イベントリスナーの削除にFunction.bind()メソッドを使う手法からコード001のスクリプトに変更。
更新日: 2013年1月27日 公開されたリリース候補版にもとづいて大幅に改訂。
作成日: 2012年12月7日


Copyright © 2001-2013 Fumio Nonaka.  All rights reserved.