Macromedia Flash非公式テクニカルノート onハンドラを使わずにロールオーバー/ロールアウトを検出する
Platform: All 1. onイベントハンドラアクションが使えない場合 しかし、大きいボタンの内側に小さいボタンを配置する場合には、意図した結果が得られない場合があります。'on'イベントハンドラアクションは、イベントを排他的に受取ります。つまり、2つのボタンが重なった領域でマウス操作をすると、手前のインスタンスしかイベントを受取れません。また、この仕様と整合性を保つため、大きいボタンの内側(の前面)に配置したボタンにロールオーバーすると、大きいボタンはマウスポインタが領域内にあるにもかかわらず'rollOut'イベントを受取ってしまいます(図1-1。サンプルSWF)。 // MovieClip: 大きいボタン
この動作は、ムービークリップシンボルにButtonインスタンスを入れ子にした場合にかぎりません。別々のButtonまたは('on'ハンドラを設定した)MovieClipインスタンスをレイヤーに分けて配置しても、重なったインスタンスの手前側がマウスイベントを排他的に受取ります。 この仕様は、たとえばポップアップメニューをつくろうとするときに、問題になります。メニューが表示された状態で、各メニュー項目のボタンは、クリックすると対応した処理が実行されます。他方で、メニューの背景イメージからロールアウトしたときに、メニューを閉じる動作にしたいことがあります(図1-2)。
背景イメージのButtonあるいはMovieClipインスタンスに'on (rollOut)'ハンドラを設定してメニューを閉じる処理を記述すると、各メニュー項目にロールオーバーしても背景のインスタンスに'rollOut'イベントが発生して、メニューが閉じてしまいます。したがって、この場合の背景イメージには、'on'ハンドラでスクリプトを書くことができないのです。 2. onClipEventハンドラでロールオーバー/ロールアウトを処理する したがって、'onClipEvent'イベントハンドラアクションでロールオーバー/ロールアウトの処理をすれば、インスタンスが重なっていても、背面にあっても問題なく対応できるということになります。しかし、'onClipEvent'ハンドラには、ロールオーバーやロールアウトのイベントがありません。「インスタンスを特定しません」ので、ロールオーバーやロールアウトの対象が定まらず、イベント自体想定できないからです。 ではどうするかというと、'onClipEvent (enterFrame)'で処理します。'enterFrame'イベントは、画面(ステージ)の描画が更新されるたびに発生します。デフォルトなら、12fpsで1秒間に12回ということになります。スクリプトによるアニメーションの処理に用いられることが多いです。この'onClipEvent (enterFrame)'で、マウスポインタの座標がインスタンス上にあるかどうかを判定して、処理を行えばよいのです。 指定した座標がMovieClipインスタンス上にあるかどうかは、'MovieClip.hitTest'メソッドで判定することができます。MovieClip上にマウス座標があるかどうかは、つぎのMovieClipアクションで判定することができます。 // MovieClip: マウスポインタがインスタンス上にあるかどうかを判定 'MovieClip.hitTest'メソッドのターゲットには、判定の対象となるMovieClipのパスを指定します。このスクリプトでは、対象のMovieClip自身にスクリプトを設定していますので、ターゲットは'this'(自分自身)でよいです。 'MovieClip.hitTest'メソッドは、第1と第2引数に判定したいxy座標をそれぞれ指定します[*1]。座標は、MovieClipインスタンスのパスにかかわらず、つねにステージを基準とします。したがって、マウスポインタの座標なら、_root._xmouseと_root._ymouseを渡します。第3引数は、オプションです。指定しないか、'flase'を渡すと、インスタンスの境界ボックスを基準として重なりを判定します。ですから、MovieClipがどのような形状であっても、矩形で判定されることになります。具体的な形状を元に重なりを判定させたいときには、第3引数に'true'を指定します。
'MovieClip.hitTest'メソッドで判定した結果、座標がMovieClipインスタンス上にあれば'true'が、インスタンス外なら'false'が返ります。ですから、'if'アクションのステートメントは座標がMovieClip上にあるときに、'else'アクションのステートメントはMovieClip外にあるときに処理されます。 上記のスクリプトで、一応マウスポインタがMovieClipインスタンス上にあるかどうかの判定はできます。しかし、テスト用に挿入した'trace'ステートメントの結果をみればわかるとおり、毎フレーム(デフォルトなら毎秒12回)、座標がインスタンス上にあるかどうかの結果を出力し続けます。つまり、同じステートメントが繰返し実行され続けてしまうということです。 'on (rollOver)'や'on (rollOut)'イベントハンドラアクションのように、マウスポインタがインスタンスに重なったとき、あるいは外に出たときに1度だけ処理が実行されるようなスクリプトを考えてみることにしましょう。 3. ロールオーバー/ロールアウトの状態の変更を判定する まず、判定の処理手順を考えます。'MovieClip.hitTest'メソッドで、マウスポインタの座標が(1)インスタンス上にあるか、(2)インスタンス外にあるかを調べます。(1)インスタンス上にあった場合は、(3)その前にインスタンス外だった、つまり変数bOutの値が'true'だったら、ロールオーバーしたことになります。ポインタが(2)インスタンス外にあった場合も、同様に(3)その前にインスタンス上にあった、つまり変数bOutが'false'だったら、ロールアウトになる訳です。 この処理手順を実際のMovieClipアクションにすると、以下のようになります。 // MovieClip: ロールオーバー/ロールアウトを検出 [1] 'onClipEvent (load)'イベントハンドラアクションで、まず変数bOutの初期値を設定します。値を'true'つまりインスタンス外の設定にしたので、このMovieClipが表示されたときマウスポインタがインスタンス外にあった場合にはロールアウトという判定はされません。'MovieClip.hitTest'メソッドの返す値が、変数bOutと同じで、状態の変更がないことになるからです。 [2] 'onClipEvent (enterFrame)'では、前述のとおりまず'MovieClip.hitTest'メソッドで、マウスポインタがインスタンス上にあるかどうかを調べます。 [3] マウスポインタがインスタンス上にあったら、その前の状態を格納した変数bOutの値が'true'かどうかを判定します。この'if'条件が当てはまる(評価が'true')なら、インスタンス外からインスタンス上に状態が変化したので、ロールオーバーの処理を行います。 [4] 状態が変更されたので、新たに現状の'false'(ポインタがインスタンス上)を変数bOutに代入します。 [5] マウスポインタが現在インスタンス上にない場合には、変数bOutの値を調べてその前の状態が'false'かを確かめます。この'if'条件が当てはまれば(評価が'true'なら)、インスタンス上からインスタンス外に状態が変化したので、ロールアウトの処理を行います。 [6] 新たな状態として'true'(ポインタがインスタンス外)を、変数bOutに代入します。 4. スクリプトを整理する
// MovieClip: ロールオーバー/ロールアウトを検出 [1] まず、'MovieClip.hitTest'メソッドの戻り値と変数bOutの値が、同じかどうかを評価しています。'MovieClip.hitTest'メソッドは、ポインタが重なっていれば'true'を返します。変数bOutは重なっていなければ、'true'に設定されます。ですから、値が同じということは、状態が変化したことを示します。 [2] ハンドラの第2ステートメントの'if'条件には、少し工夫を加えています。条件に指定した式は、論理式ではなく代入式であることにご注意ください(前掲『オブジェクト指向で考えるActionScript』p.94〜コラム「ifアクションの条件」参照)。'!'演算子は論理否定で、ブール(論理)値を反転します。その値を変数bOutに代入しているので、結局変数値の'true'/'false'が入替わることになります。 'if'条件に代入式を指定すると、代入された値を評価します。つまり、値反転後の変数bOutが'true'か'false'かを調べることになるのです。前の[1]の'if'条件でマウスポインタの重なり状態が変化したときにこの'if'アクションが処理されるので、値反転後の変数bOutは現在の状態を示すことになります。したがって、'if'条件の評価が'true'ならロールアウト、'false'ならロールオーバーの処理を行います。 このスクリプトをベースに作成したポップアップメニューのサンプルSWFを、ご参考までに掲げます。なお、ソースのFLAファイル(Zip圧縮/約7KB)もダウンロードできます(SWFを再生するにはFlash Player 6以降、FLAファイルを開くにはFlash MX以降が必要です)。 なお、2で作成したスクリプトでも、'if'アクションは条件に当てはまるもののみが処理されます。ですから、ステートメントの数が多くても、非効率な処理を行っているということではありません。 _____ 作成者: 野中文雄 Copyright © 2001-2003 Fumio Nonaka. All rights reserved. |
||||