feb19.jp

Nobuhiro Takahashi
Designer / Engineer

AS3のEvent.REMOVEDがなかなか便利なので使用方法と注意点

AS3のEvent.REMOVEDがなかなか便利なので使用方法と注意点

Tweenerとか、FMSのストリーミングとかを使うときにとても便利かも。他にも応用範囲はいくらでもありそう。そんな訳で、「インスタンスがremoveChildされた時/する時」を取ることができるイベント定数「Event.REMOVED」についてのメモと、実例。中盤はAS3のイベントフローについての解説になっています。ついでにREMOVED_FROM_STAGEも解説。

例えば、Tweenerは、トゥイーンされているオブジェクトをある瞬間に破棄(removeChild)しても、Tweener内部ではEvent.ENTER_FRAMEが動き続けていて、onCompleteを設定していた日にはその関数が呼ばれてしまうということがあったりします。(もう少し細かく言えば、TweenerはaddTweenした瞬間に内部でnew MovieClipしていて、そのムービークリップ参照のENTER_FRAMEイベントを動かしているので、参照が強力で消えないから、ということっぽいです)

また、FMSのストリーミングでは、FMSのストリームをステージ上のVideoインスタンスにNetStream.attachNetStreamをして関連づけることで映像を見せているわけですが、ステージ上のVideoインスタンスやそれを含むSWFをremoveChildしたとしても、ストリームは残り続けてしまい、Videoインスタンスが消えて映像は消えても、音声は再生されつづけてしまいます。

なので、前者はTweener.removeTweens(removeするインスタンス)、後者はNetStream.close(); NetConnection.close()してやらないといけません。

そんなときに便利なのが、「表示オブジェクトがステージまたはステージが含むインスタンスからremoveChildされた/する時に発生するイベント」Event.REMOVEDを使うと便利です。

import caurina.transitions.*;
mc.addEventListener(Event.REMOVED, onRemovedHandler);
btn.buttonMode = true;
btn.addEventListener(MouseEvent.CLICK, onClickHandler);
Tweener.addTween(mc, {time:50, useFrames:true, x:400, transition:"easeOutQuint", onComplete:onCompleteHandler});
function onClickHandler(e:MouseEvent):void {
	removeChild(mc);
}
function onRemovedHandler(e:Event):void {
	Tweener.removeTweens(mc);
}
function onCompleteHandler():void {
	trace("next!")
	if(mc.x == 400) {
		Tweener.addTween(mc, {time:50, useFrames:true, x:200, transition:"easeOutQuint", onComplete:onCompleteHandler});
	} else {
		Tweener.addTween(mc, {time:50, useFrames:true, x:400, transition:"easeOutQuint", onComplete:onCompleteHandler});
	}
}

ひたすらmcが左右に動く。ターンするときに、"next"とトレースするSWF。btnをクリックすると、mcを表示リストから外し、トゥイーンを破棄します。

ただ、注意すべき点があって、このEvent.REMOVEDというのは、「消された!」っていうのと「消した!」っていう意味を持っているので、例えばこのmcの中にmc2というMovieClipがあって、ボタンを押すときにremoveChildするMovieClipはmc2にするプログラムに変更した場合、mcは消されてもいないのにmc2を消したということで、Event.REMOVEDを受け取ってしまい、ループするTweenerが止まってしまうということがあります。

そこで、AS3のイベントモデル:イベントのフェーズ(段階)とイベントのターゲットを分岐することで判別します。

AS3のイベントは、イベントを受け取るmcやbtnにaddEventListenerをすることで、その表示オブジェクトがイベントを受け取ることができますが、イベントフローには段階があります。まず、そのイベントのメッセージはステージから順番に奥の階層に向かって伝達されていきます(この子をremoveしてくださいねー)。この段階を「キャプチャ段階」といいます。そして、イベントを受け取る表示オブジェクトにイベントのメッセージが到達して処理している(はいあなたremoveしてくださいねー)段階のことを「ターゲット段階」、そしてステージに向かってイベントが戻っていく(お宅の子をremoveしましたよー表示リスト更新してくださーい)段階を「バブリング段階」といいます。

この「はいあなたremoveしてくださいねー」の「ターゲット段階」の時はまさにremoveChildされるそのもののオブジェクトのことをさすので、そのタイミングでターゲット名を判別すればいけます。

イベントフローの判別は、イベント関数に引数に用意するeとかevtとかeventとか任意のイベント型変数に.eventPhaseで調べられます。1がキャプチャ段階、2がターゲット段階、3がバブリング段階。

というわけで、まずbaseというMovieClipがあって、add_btn、remove_btn、baseRemove_btnがステージに置いてあるSWFのコード。remove_btnやbaseRemove_btnの挙動を探ってみてください。

var s:Sprite = new Sprite();
s.name = "sprite";
base.root.addEventListener(Event.REMOVED, onRemovedHandler);
base.addEventListener(Event.REMOVED, onRemovedHandler);
s.addEventListener(Event.REMOVED, onRemovedHandler);
//--
add_btn.buttonMode = true;
remove_btn.buttonMode = true;
baseRemove_btn.buttonMode = true;
add_btn.addEventListener(MouseEvent.CLICK, onClickHandler);
remove_btn.addEventListener(MouseEvent.CLICK, onClickHandler);
baseRemove_btn.addEventListener(MouseEvent.CLICK, onClickHandler);
//--
function onRemovedHandler(e:Event):void {
	trace("target: " + e.target.name);
	trace("currentTarget: " + e.currentTarget.name);
	trace("eventPhase: " + e.eventPhase);
	trace("type: " + e.type);
	trace("---------");
	if(e.eventPhase == 2 && e.target.name == "base") {
		trace("base removed!")
	}
}
function onClickHandler(e:MouseEvent):void {
	switch(e.currentTarget) {
		case add_btn:
			base.addChild(s);
			break;
		case remove_btn:
			base.removeChild(s);
			break;
		case baseRemove_btn:
			removeChild(base);
	}
}

root、base、spriteがEvent.REMOVEDイベントの受け取りを待機していますが、baseRemove_btnを押したときは、spriteは反応せず、baseとrootが反応します。また、baseがターゲット段階で、rootがバブリング段階にイベントを受け取るので、baseがremoveChildされたときだけ"base removed!"という文字をトレースします。

ただ、ここで気になる、baseを削除した時、子のspriteはEvent.REMOVEDイベントに反応させることはできないかという疑問。

そうしたい場合は、Event.REMOVEDを使うのではなく、Event.REMOVED_FROM_STAGEを使うと便利です。

上のサンプルコードで、Event.REMOVEDとなっているところを、Event.REMOVED_FROM_STAGEにすると、removeChildによって消されるオブジェクトの子のオブジェクトにもイベントが送られるので(画面の表示上から消えるオブジェクトに直接イベントメッセージが来てる感じなので、キャプチャ段階とかバブリング段階が存在しない感じ)、サンプルは、消されたタイミングで消されたオブジェクトのみが反応します。トレースの結果を上と比べてみてください。

そんなわけで、テレビCMコーナーはFMSでやりますのでよろしくお願いしますとか言われた時は、映像を表示するVideoオブジェクトまたはその親のSWFがEvent.REMOVED_FROM_STAGEされたときは、NetSream.close();、NetConnection.close();する、ということにすればなかなかスマートっぽいです。

Tweet Share Bookmark

Navigation

prev: AS3のInteractiveObject.mouseEnabledにご注意
next: デフォルトのコンテキスト(右クリック)メニューを非表示にする

Recently Entries