前回の第30回「マウスイベントとインスタンスの兄弟・親子の関係」に引続き、今回もマウスイベントの扱いをお題にする。とくに、マウスのロールオーバーをネタに、複数のインスタンスを含んだマウスイベントの捉え方について解説したい。
InteractiveObject.mouseOverとInteractiveObject.rollOverイベント
マウスのロールオーバーを捉えるイベントには、InteractiveObject.mouseOver(定数MouseEvent.MOUSE_OVER)とInteractiveObject.rollOver(定数MouseEvent.ROLL_OVER)がある(※1)。このふたつのイベントの違いについて調べるムービーをつくってみよう。MovieClipシンボル(シンボル名Parent)の中に、ふたつの MovieClipインスタンスを入れ子にする。ふたつのインスタンス名は、それぞれpen0_mcとpen1_mcだ(図1)。
このMovieClipシンボルのインスタンスをひとつメインタイムラインに配置し、インスタンス名はparent_mcとする。イベントInteractiveObject.mouseOverとInteractiveObject.rollOverとは、そのターゲットのインスタンスをどう捉えるかが違っている。親のMovieClipインスタンスparent_mcの第1フレームアクションに以下のスクリプト1を書き、ふたつのイベントにリスナー関数xTraceを登録する(図2)。
リスナー関数xTrace()は、受取ったイベントオブジェクトから、3つのプロパティを取出す。Event.typeとEvent.targetおよびEvent.currentTargetプロパティだ。ただし、後のふたつはインスタンスが区別できるようDisplayObject.nameプロパティを調べたうえで、trace()関数により3つの情報を[出力]する。なお、シンボル内に入れ子にしたふたつのインスタンスには、名前をつけたほかはとくに設定はしていない。
[ムービープレビュー]して、入れ子の一方のMovieClipインスタンス(pen0_mc)にマウスポインタを重ねると、イベントInteractiveObject.mouseOverとInteractiveObject.rollOverがともに発生する。ただし、つぎのようにEvent.targetプロパティのインスタンスの[出力]が異なる。
InteractiveObject.mouseOverイベントではマウスポインタを重ねた子のインスタンスが捉えられているのに対し、InteractiveObject.rollOverイベントはリスナーを登録した親に発生したものと扱われるのだ。
さらに、マウスポインタを他方のインスタンス(pen1_mc)の上に移すと、InteractiveObject.mouseOverイベントはインスタンスpen1_mcに対して改めて発生する。しかし、InteractiveObject.rollOverイベントは起こらない(図3)。つまり、InteractiveObject.mouseOverイベントは子インスタンスごとに、InteractiveObject.rollOverイベントはリスナーが登録されたインスタンスをひとつとみなして発生するのである。
子のインスタンスに対するマウスイベントの発生を止める
マウスのロールオーバーについては、子インスタンスごとにイベントを受取りたいときはInteractiveObject.mouseOver、リスナーを登録した親インスタンスでまとめて捉えたいときにはInteractiveObject.rollOverと、ふたつを使い分ければよいと一応はいえる。しかし、InteractiveObject.mouseOverイベントを使いつつ、子インスタンスごとではなく、親インスタンスでまとめて扱いたい場合があるかもしれない。
それだけでなく、マウスクリックを捉えるInteractiveObject.clickイベントも、実は子インスタンスごとに発生する。つまり、前述のムービーのようにふたつの子インスタンスが入れ子になっている場合(前掲図3参照)、一方の子インスタンス上でマウスボタンを押したまま、ポインタを他方のインスタンス上に移してボタンを放したとき、InteractiveObject.clickイベントは発生しない。しかも、ロールオーバーと違って、クリックを親インスタンスでまとめて受取るイベントというものは別に用意されてない。
そうなると、子インスタンスごとにイベントを発生させない手段が知りたい。ひとつは、前回紹介したInteractiveObject.mouseEnabledプロパティをfalseにすることだ。すると、子インスタンスへのマウスイベントは親インスタンスが直接受取れる。ただし、その設定をしていない子インスタンスは、相変わらず自分に対するマウスイベントと捉えてしまう。したがって、マウスイベントを親インスタンスがまとめて受取るためには、すべての子インスタンスのInteractiveObject.mouseEnabledプロパティをfalseにしなければならない(図4)。
もうひとつ、子インスタンスごとにマウスイベントが発生しないように、親インスタンスのプロパティを使って設定できる。DisplayObjectContainer.mouseChildrenプロパティをfalseにすると、その子インスタンスはマウスイベントを自分に対して受取らなくなり、親インスタンスへのイベントとして扱われる(図5)。すると、InteractiveObject.mouseOverイベントも、InteractiveObject.rollOverと同じ動きになる。
サムネイルへのロールオーバーで表示画像を切替える
ロールオーバーを使う練習として、サンプルムービーをひとつつくってみよう。ステージの端にサムネイル画像を並べて、それらにロールオーバーしたとき、対応する大きい画像の表示を切替える(図6)。サムネイルと表示画像とをどう結びつけるかが、ひとつのポイントだ。
サムネイルは3つMovieClipインスタンスで並べ、インスタンス名はbutton0_mcからbutton2_mcとした。大きい表示画像も予めタイムラインに重ねて置いておき、DisplayObject.visibleプロパティにより表示を切替えることにする。MovieClipインスタンス名は、サムネイルと番号を対応させて、my0_mcからmy2_mcとしておこう。
以下のフレームアクションは、サムネイルのインスタンスにロールオーバーすると、対応する大きい画像の表示を切替える(スクリプト2)。サムネイルのインスタンス(button0_mc~button2_mc)には、それぞれのInteractiveObject.rollOverイベント(定数MouseEvent.ROLL_OVER)に同じリスナー関数xShow()を登録した。
サムネイルと表示画像を結びつけるために、すべてのサムネイルのインスタンス(button0_mc~button2_mc)に同じ名前の変数(show_mc)を設定し、そこに対応する表示画像のインスタンスを納めた。リスナー関数xShow()は、引数に受取ったイベントオブジェクトのEvent.currentTargetプロパティからロールオーバーしたサムネイルのインスタンスを取出し、その変数に納められた対応する画像のインスタンスを表示するという仕組みだ。
もうひとつちょっとしたテクニックは、トグルボタンのようにひとつをオンにしたら他はすべてオフにするという処理だ。このサンプルでひとつの画像を表示したら、他はすべて非表示にしなければならない。そういうとき、一旦すべてをオフつまり非表示にしてから、改めて対象のものだけオンつまり表示すればよい。こうすると、今どれがオンになっているのかということも気にしなくて済む。
関数xClearAll()は、すべての画像を非表示にする。リスナー関数xShow()はこの関数を呼出したうえで、変数から取得した対応画像の表示を行っている。なお、画像のアウトラインがわかりにくいものもあるので、グローとドロップシャドウのフィルタもかけた。このサンプルであれば、フィルタはオーサリング時に予め設定しておいても構わない。ダイナミックなフィルタの適用と破棄は、いわば練習として加えてみた。
[ムービープレビュー]を確かめると、ロールオーバーしたサムネイルに応じて、左側の大きい表示画像が切替わる(図7)。
ActionScript 1.0や2.0では、異なるインスタンスを結びつけるためにインスタンス名がよく使われた(※2)。ActionScript 3.0でも、その手法は一応使える。しかし、DisplayObject.nameプロパティは、trace()関数で[出力]してインスタンス名を確かめる以外には、あまり使い勝手はよくない。
ActionScript 3.0では、できるだけインスタンスの参照を使う仕組みにする方がよいだろう。このサンプルでは、MovieClipインスタンスの変数に参照を加えた。マウスイベントのお題は今回で終える。しかし、次回も同じサンプルをネタに、インスタンスの参照の扱いについて考えてみたい。つぎのお題は、 Dictionaryクラスだ。
今回解説した次のサンプルファイルがダウンロードできます。