EaselJSの次期バージョンに加わるイベントを扱うふたつのメソッドと注意点 [Edit]

EaselJSの次期バージョンでイベント周りの扱いが大きく変わります(「EaselJS次期バージョンのイベントモデル改訂」参照)。EventDispatcherクラスをDisplayObjectクラスが継承し、イベントオブジェクトの基本クラスとしてEventクラスが定められました。これらのクラスに新たに備わった便利なメソッドをふたつご紹介します。

The EaselJS NEXT version change its event model (CreateJS Blog "Event Model Updates"). The EventDispatcher class is inherited by the DisplayObject class. And the Event class is newly added as the base class of event objects. This article explains the following two new methods in these classes.

  • EventDispatcher.on()
  • Event.remove()

01 EventDispatcher.on()

EventDispatcher.on()メソッドは、EventDispatcher.addEventListener()メソッドと同じく、オブジェクトにリスナーを加えます。さらに引数が加わり、リスナー関数内のthis参照や1度だけの呼出し、リスナー関数に渡す第2引数なども与えられます。

The EventDispatcher.on() method adds a listener to the refered object as well as the EventDispatcher.addEventListener() does. But the method has more arguments to specify scope, calling the listener once, the second argument to be passed to the listener function, and so on.

object.on(type, listener, scope, once, data, useCapture)

注意しなければならないのは、EventDispatcher.on()メソッドが引数から新たな関数をつくり、それをリスナーとして加えることです。つまり、メソッドの第2引数に渡した関数は、リスナーではありません。新たな関数はメソッドが返します。ですから、リスナーを除くときは、その戻り値の参照を用いなければなりません。

Note that EventDispatcher.on() method creates new function for the listener from its arguments. Therefore, the second argument's function of the method is not the listener. The created listener function is returned from the method. Then the returned reference should be used when the listener is removed.

たとえば、以下のコードはインスタンス(instance)をクリックするたびに30度回し、2回目のイベントでリスナーが除かれます。EventDispatcher.on()メソッドの第3引数にObjectインスタンス(data)を与え、そのプロパティ(count)にカウンタの整数を定めました。また、第5引数にはリスナー関数に渡す数値(30)を加えました。なお、EventDispatcher.off()メソッドは、内部的にEventDispatcher.removeEventListener()を参照します。

The example code below rotates the instance (instance) 30 degrees on each click and removes the listener on the second click. The Object instance (data) of which property (count) has counter integer is passed as the third argument of EventDispatcher.on() method. Its fifth argument (30) is angle to be passed the listener function. The EventDispatcher.off() method internaly referes the EventDispatcher.removeEventListener().

var listener;
var data = {};
listener = instance.on("click", clickHandler, data, false, 30);
data.count = 0;
function clickHandler(eventObject, angle) {
	var instance = eventObject.target;
	instance.rotation += angle;
	if (++this.count > 1) {
		instance.off("click", listener);
	}
}

02 Event.remove()

Event.remove()メソッドは、そのイベントから呼出されたリスナー自身を除きます。Eventオブジェクトを参照するだけでよく、イベント名(種類)もリスナー関数の参照も要りません。

The Event.remove() method removes the called listener from the event. The necessary reference for the method is only the Event object. Its event type nor target object are not needed.

eventObject.remove()

すると、前掲コードのリスナー関数(clickHandler)は、つぎのようにEvent.remove()を用いて書替えることができます。

Then the listener function (clickHandler) in the example code above can be revised with the Event.remove() as follows:

function clickHandler(eventObject, angle) {
	var instance = eventObject.target;
	instance.rotation += angle;
	if (++this.count > 1) {
		// instance.off("click", listener);
		eventObject.remove();
	}
}

ただし、注意しなければならないのは、Event.remove()メソッドを呼出しても、イベントリスナーは直ちには削除されないことです。内部的にはEventオブジェクトにフラグが立てられて、つぎにイベントが配信されるときに除かれます。そのため、Event.remove()メソッドを呼出したすぐ後にEventDispatcher.hasEventListener()の戻り値を調べるとtrueが返されます。

But note that the listener will not be instantly removed when the Event.remove() method is called. Internally the flag to remove is set to the event object and the listener is deleted when the next event is dispatched. Therefore, just after the Event.remove() method is called, EventDispatcher.hasEventListener() returns true.

03 EventDispatcher.on()の改定案 - Revision of EventDispatcher.on()

筆者は、EventDispatcher.on()メソッドが新たにつくるリスナー関数の引数として、つぎのようにリスナー関数自身の参照を加えてはどうかと考えます。

My idea to revise the EventDispatcher.on() is to add the reference of the listener function, which the method creates and returns, as its third argument.

createjs.EventDispatcher.prototype.on = function(type, listener, scope, once, data, useCapture) {
	if (listener.handleEvent) {
		scope = scope || listener;
		listener = listener.handleEvent;
	}
	scope = scope || this;
	var args = [];
	var newListener = function(evt) {
		args[0] = evt;
		listener.apply(scope, args);
		once && evt.target.removeEventListener(evt.type, newListener, useCapture);
	};
	args[1] = data;
	args[2] = newListener;
	/*
	return this.addEventListener(type, function(evt) {
			listener.call(scope, evt, data);
			once && evt.remove();
		}, useCapture);
	*/
	return this.addEventListener(type, newListener, useCapture);
};

すると、前掲コードのリスナー関数(clickHandler)は、つぎのように関数自身をイベントリスナーから除けます。

Then the listener function (clickHandler) in the example code above can be removed itself from the event as follows:

// var listener;

// function clickHandler(eventObject, angle) {
function clickHandler(eventObject, angle, listener) {
	var instance = eventObject.target;
	instance.rotation += angle;
	if (++this.count > 1) {
		// eventObject.remove();
		instance.off("click", listener);
	}
}

その他の記事