2009年02月07日

実用? Dockっぽいメニュー

続・MacのDockみたいなに書いたDockっぽいメニューを実用化してみました。

例では、各アイコンをクリックしたときに対応する単語を表示するようにしていますが、ページ移動するような関数を与えれば(後述)、サイトのメニューなんかにも使えるかと。

FlashPlayer9以上が必要です。

ソースコードと簡単な解説は続きに。

コンテナクラスに実行したい関数を渡し、クリック時に実行するよう、イベントを追加しています。 Icon0などは外部に定義。

package {
    import flash.display.Sprite;
    import flash.display.MovieClip;
    import flash.display.Stage;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.utils.*;
    
    public class Main extends Sprite {
        public function Main() {
            // 台座
            var base:Sprite = new Sprite();
            base.graphics.beginFill(0x555555);
            base.graphics.drawRect(10, 85, 380, 30);
            base.graphics.endFill();
            stage.addChild(base);
            
            // アイコン
            var icons :Array = new Array();
            icons[0] = new Container(stage, new Icon0(), 0 * 60 + 50, 75, function() :void { /* 実行したい処理 */ });
            icons[1] = new Container(stage, new Icon1(), 1 * 60 + 50, 75, function() :void { /* 同上 */ });
            icons[2] = new Container(stage, new Icon2(), 2 * 60 + 50, 75, function() :void { /* 同上 */ });
            icons[3] = new Container(stage, new Icon3(), 3 * 60 + 50, 75, function() :void { /* 同上 */ });
            icons[4] = new Container(stage, new Icon4(), 4 * 60 + 50, 75, function() :void { /* 同上 */ });
            icons[5] = new Container(stage, new Icon5(), 5 * 60 + 50, 75, function() :void { /* 同上 */ });
        }
    }
}

import flash.events.Event;
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.display.Stage;
import flash.events.MouseEvent;

class Container extends Sprite {
    const MEASURE = 100;
    
    private var mStage:Stage;
    private var mIcon:MovieClip;
    private var mX:Number;
    private var mY:Number;
    
    public function Container(stage:Stage, icon:MovieClip, x:Number, y:Number, click :Function) :void {
        mStage = stage;
        mIcon = icon;
        mX = x;
        mY = y;
        
        icon.x = mX;
        icon.y = mY;
        this.addEventListener(Event.ENTER_FRAME, this.scaleChange);
        mStage.addChild(mIcon);
        
        // クリック時に渡された関数を実行するイベントを追加
        mIcon.addEventListener(MouseEvent.CLICK, function() :void {
            click.call(null);
        });
    }

    private function scaleChange(evt:Event) :void {
        // カーソルとアイコンの距離に応じて倍率を変える
        var scale:Number = (2 * MEASURE - Math.abs(mIcon.x - mStage.mouseX)) / MEASURE;
        if(scale < 1.0) scale = 1.0;
        mIcon.scaleX = scale;
        mIcon.scaleY = scale;
        mIcon.y = mY + (mIcon.height / 4) * (1.0 - scale);
        
        // X位置を変化
        var dx:Number = (mIcon.x - mStage.mouseX) / MEASURE;
        mIcon.x = mX + dx * Math.exp(-dx * dx) * MEASURE / 4;;
    }
}

20~25行目がアイコンの定義です。 引数は前から順に、現在のステージ、X位置、Y位置、クリック時に実行する関数、となっています。

for文でループさせればもっときれいに書けるんですが、たとえば

var url :Array = new Array();
url[0] = 'http://hextomino.tsukuba.ch';
/* 略 */

for(var i = 0; i < ICONNUM; i++) {
    new Container(stage, new Icon0(), 0 * 60 + 50, 75, function() :void { /* url[i]にページ遷移する処理 */ });
}

のようにすると、常にurl[i] = url[7] ("現在の"iの値)が参照されてしまって、うまくいきません。 Iconクラス内にあらかじめ処理を持たせておく、という方法もありますが、それだとちょっと見通しが悪くなるかと思ったので。

上のコードではContainerクラス自信でstageにアイコンを追加するようにしているので、インスタンス生成時にstageを渡していますが、Containerに保持されているアイコンを返すクラスを用意して、それを使って自分でstageに追加するようにしてもいいかもしれません。 その場合Containerクラスの引数、stageは不要になります。



タグ :ActionScript

同じカテゴリー(プログラミング)の記事画像
GoogleReaderを3ペイン表示にするスクリプトのα版
文字サイズを拡大縮小するスクリプト
電卓を表示するブックマークレット
Twitterで費やした時間を表示するスクリプト
ニコニコのタグをプレビューするスクリプト
ニコニコで広告を消すスクリプト
同じカテゴリー(プログラミング)の記事
 GoogleReaderを3ペイン表示にするスクリプトのα版 (2009-07-26 22:00)
 クリック動作を無効にするジョークブックマークレット (2009-07-19 23:52)
 文字サイズを拡大縮小するスクリプト (2009-07-12 18:09)
 ダブルクリックでスクロールするスクリプト (2009-07-05 14:22)
 電卓を表示するブックマークレット (2009-06-27 21:40)
 ごくごく一部の顔文字を絵文字に置き換えるスクリプト (2009-05-21 23:06)
Posted by Handle at 12:44│Comments(3)プログラミング
この記事へのコメント
こんにちは。
ありがとうございます!

早速コードを追加して試してみました。

それで、Containerクラスへ追加された分は56行~58行だけですよね?
実行すると、
「1120: 未定義のプロパティ MouseEvent へのアクセスです。」というエラーがでて、↓の箇所をさしています。

”mIcon.addEventListener(MouseEvent.CLICK, function() :void { ”

これは、どうしてでしょうか?

私がまだAS初心者なので、ちょっとしたエラーでも直ぐに躓いてしまいます。
お手数ですけど、よろしくお願いします。
Posted by me at 2009年02月09日 11:32
> me さん

自分も初心者から抜け出せない人間ですが、できる限りお答えさせていただきます(^^;

Containerクラスに追加したのは、おっしゃるとおり56~58行目の、イベントをハンドルする部分だけです。
あ、あとクリック時に実行する関数を受け取るために、コンストラクタの引数を一つ増やしてますね。

今回のエラーの場合、MouseEventが見つからないことになっていますが・・・。
MouseEventのimportを忘れてる・・・訳じゃなさそうですね。
その場合エラーメッセージは「型が見つからない」みたいになると思うので。

もしかしたらクリック時に実行する関数が、引数を受け取るようになっていないのが原因かもしれません。
56行目を

mIcon.addEventListener(MouseEvent.CLICK, function(evt :MouseEvent) :void {

のように変えて試してみてはいただけないでしょうか。
あるいはメインのクラスの方にもMouseEventをimportしてみるとか・・・。

うちの環境(FlashDevelop+FlashCS3)では特に問題がなかったので、こちらで原因を特定するのはちょっと厳しいかもしれません。


見当違いかもしれませんが、ひとまずこれを回答とさせてください。
Posted by HandleHandle at 2009年02月09日 12:32
こんにちは。

>mIcon.addEventListener(MouseEvent.CLICK, function(evt :MouseEvent) :void {

>のように変えて試してみてはいただけないでしょうか。
>あるいはメインのクラスの方にもMouseEventをimportしてみるとか・・・。

こちらですが、変えてもエラーが表示されてしまいました。
それで、Handleさんの環境で何のエラーも出ない事言う事だったので、もしかすると自分の作っていたプログラムの中に入れ込んでいたので何かしら不具合が出たのかと思いました。

あと、自分のプログラムに”クリックしたときの動作”を既に設定していたので、できればそこへつなげられたらと思いながら、そのソース内に
44行目からの
# public function Container(stage:Stage, icon:MovieClip, x:Number, y:Number, click :Function) :void {
#



を切り取って埋め込んだりなどして、ごちょごちょと触っていたら、動作するようになりました。
かなり、無理やりな箇所もあるので、これからプログラム整理しようかと思います。(汗

Handleさんのソースがあったからこそ、道が開けて本当に感謝しています。

ありがとうございます m(_ _)m
Posted by me at 2009年02月10日 14:15
コメントフォーム
上の画像に書かれている文字を入力して下さい
<ご注意>
書き込まれた内容は公開され、ブログの持ち主だけが削除できます。