プログラマのためのFlash遊び方

第7回Flashでジェネレータを作ってみよう

さて、今回はいままでの集大成として、少し凝った Flash を作成します。いきなりですが、完成品をご覧いただきましょう。Web 2.0風ロゴジェネレータです。

テキスト欄の文字を編集してみてください。リアルタイムにロゴが更新されます。

ジェネレータといえば、ボタンを押して少し待ってから結果が表示されるものがほとんどですが、Flashの表現力を持ってすれば、リアルタイムに反映されるものが作れるのです。

コンパイルしよう

今回のソースコードは全体で200行近くあります。2つのファイルを同じフォルダにダウンロードしてください。

mxmlcを実行してコンパイルします。

mxmlc -default-background-color=0xffffff LogoGenerator.as

コンパイルオプションに-default-background-color=0xffffffをつけています。これはデフォルトの背景色を白色に設定するものです。今回のサンプルは、背景が白色であることを前提にしているため、このように指定しています。

コンパイルオプションでWeb2Badge.asを指定していませんが、LogoGenerator.asの中でWeb2Badgeクラスを参照しているため、Web2Badge.asも一緒にコンパイルされます。

ソースコードの全体を把握する

個別の部分を見てく前に、LogoGenerator.asの全体を把握しておきましょう。LogoGeneratorクラスが定義されています。

package {
    import ...

    // LogoGenerator クラス
    public class LogoGenerator extends Sprite {
        // インスタンス変数の宣言
        private var inputText:TextField;   // テキスト入力欄
        private var reflection:Bitmap;     // 反射効果
        private var preview:Sprite;        // プレビュー表示
        private var previewText:TextField; // プレビュー文字
        private var hilight:Sprite;        // 明るくするハイライト
        private var badge:Web2Badge;       // BETA のバッジ

        // コンストラクタ
        public function LogoGenerator():void {...}

        // 表示オブジェクトの初期化
        private function initComponents():void {...}

        // 表示を更新する
        private function update():void {...}

        // 反射を更新する
        private function updateReflection():void {...}
    }
}

このクラスは6個のインスタンス変数とコンストラクタ、3つのメソッドから成り立っています。

コンストラクタ

最初にコンストラクタの処理をざっと見ていきます。

// コンストラクタ
public function LogoGenerator():void {
    // 左上に固定して拡大縮小されないよう指定
    stage.scaleMode = "noScale";
    stage.align = "TL";

    // 各コンポーネントを初期化する
    initComponents();

    // 初回の描画を行う
    update();

    // イベント登録を行う
    inputText.addEventListener("change", function(event:Event):void {
        update();
    });
}

Flashは表示する中身が埋め込まれたときのサイズに収まるように、自動的に拡大縮小されます。これを防ぐために、最初にstageのscaleModeプロパティを"noScale"(拡大縮小しない⁠⁠、alignプロパティを"TL"(左上に配置)と設定しています。

次に、自分で定義したinitComponents()メソッドとupdate()メソッドを呼んで、表示の初期化を行っています。最後に、テキスト入力欄の内容が変更されたときにupdate()メソッドが呼ばれるようにイベント登録しています。

それでは、それぞれのメソッドの処理を見ていきましょう。

表示オブジェクトの初期化

initComponents()は表示オブジェクトを初期化するメソッドです。Flashの表示開始時に、コンストラクタから呼ばれます。

初期化が完了すると、表示オブジェクトの階層は次のようになります。

画像

initComponents()メソッドでは、LogoGeneratorオブジェクトの子どもの6つの表示オブジェクトを順番に作成していきます。内容に応じた初期化処理を行い、addChild()メソッドを利用して表示オブジェクトの階層を構築します。

それぞれの表示オブジェクトを順番に見ていきましょう。

テキスト入力欄(inputText)

文字列を入力するTextFieldクラスのオブジェクトです。

TextFieldクラスのtypeプロパティに"input"を指定することで、編集可能な文字列となります。

inputText = new TextField();
inputText.border = true;
inputText.type = "input";

反射効果(reflection)

床に反射したような効果を表示するBitmapオブジェクトです。

initComponents()メソッドではインスタンスの作成のみを行っています。実際の描画はupdateReflection()メソッドで行っています。

プレビュー表示(preview)

この表示オブジェクト自体は何も表示しませんが、子の表示オブジェクトとして、⁠プレビュー文字」⁠明るくするハイライト」⁠BETAのバッジ」の3つを格納しています。

// プレビュー用の Sprite を生成
preview = new Sprite();
preview.y = 50;

...

preview.addChild(previewText);
preview.addChild(hilight);
preview.addChild(badge);

3つの表示オブジェクトを格納しているのは、反射効果を描画するときに、まとめてBitmapDataに描画する必要があるからです。詳しくは後述します。

プレビュー文字(previewText)

テキスト入力欄に入力された文字を、大きく青色で表示するTextFieldオブジェクトです。

defaultTextFormatプロパティを指定して、文字のサイズと色を変更しています。

var tf:TextFormat = new TextFormat();  // TextFormat オブジェクトを作成
tf.size = 48;                          // 文字サイズを 48px に変更
tf.color = 0x3292d8;                   // 色を #3292d8 (青色) に変更
previewText.defaultTextFormat = tf;    // TextFormat を指定

明るくするハイライト(hilight)

文字の上側半分を明るい色にしているのがこの表示オブジェクトです。

半透明の白い図形を文字の上に重ねることで、文字の一部分が明るく見えます。

hilight = new Sprite();
hilight.graphics.beginFill(0xffffff, 0.3);
hilight.graphics.drawRect(0, 0, 10, 10);
hilight.graphics.drawEllipse(0, 3, 10, 10);
hilight.graphics.endFill();

円と四角を重ねて描画しています。重なった部分は塗りがなくなります。そのため、のように、四角の下半分が円状にくり抜かれます。

画像

update()メソッドでは、この表示オブジェクトを文字のサイズに合わせて拡大しています。

BETAのバッジ(badge)

文字の横に表示されるBETAのバッジです。

バッジの描画は複雑なので、Web2Badgeクラスを別途定義して、そちらで処理を行っています。initComponents()メソッドでは、Web2Badgeクラスのインスタンスを作成しています。

badge = new Web2Badge();
badge.y = 10;
...
preview.addChild(badge);

Web2Badgeクラスの実装は第3回のものとほとんど同じです。ただし、第3回ではSpriteオブジェクトを作成して、そこに描画していましたが、今回はWeb2Badgeオブジェクトに直接描画しています。

位置とサイズの更新

update()メソッドは表示オブジェクトの位置やサイズを更新します。コンストラクタから1回呼ばれたあとは、テキスト入力欄の文字が変更されるたびに呼び出されます。

udpate()メソッドの実装は次のようになっています。

// 表示を更新する
private function update():void {
    // 文字列の更新
    previewText.text = inputText.text;            --(1)

    // 最後の1文字の色を変更する
    var tf:TextFormat = new TextFormat();                    
    tf.color = 0xfd1e73;                                     |(2)
    previewText.setTextFormat(tf, inputText.text.length - 1);
    
    // ハイライトのサイズを変更する
    hilight.width = previewText.textWidth;         (3)
    hilight.height = previewText.textHeight * 1.5; 

    // バッジの位置を移動する
    badge.x = previewText.textWidth + 20;         --(4)

    updateReflection();                           --(5)
}

上記コード中の行末の注釈は次のとおりです。

(1)
プレビュー文字をテキスト入力欄に入力された文字列に変更します。
(2)
setTextFormat()メソッドを利用して、最後の1文字を0xfd1e73(ピンク色)に変更しています。
(3)
明るくするハイライトをプレビュー文字の大きさに合わせて拡大しています。
(4)
BETAのバッジを文字列の右側に移動しています。
(5)
反射効果を更新します。

反射効果の描画

udpateReflection()メソッドではBitmapDataクラスを利用して反射効果を描画してます。BitmapDataクラスは読んで字のごとく、ビットマップ画像を扱うためのクラスです。

Flashはベクターグラフィックのイメージが強いので、ビットマップという言葉には違和感があるかもしれません。BitmapDataはピクセル単位で見た目を修正できるので、ベクターだけでは難しい効果を実現することができます。

BitmapDataオブジェクトの作成

まず、BitmapDataオブジェクトを作成します。コンストラクタには画像の縦横のサイズを指定します。

var bmd:BitmapData = new BitmapData(preview.width, previewText.height * 2);

ここでは、プレビュー表示2つ分の領域を持ったBitmapDataを作成しています。

次に、fillRect()メソッドで、全体を白色(0xffffffff)に塗ったあと、draw()メソッドでプレビュー表示をBitmapDataオブジェクトに描画しています。

bmd.fillRect(bmd.rect, 0xffffffff);
bmd.draw(preview);

プレビュー表示には、⁠プレビュー文字」⁠明るくするハイライト」⁠BETAのバッジ」の3つが格納されているため、これら3つの内容がBitmapDataに描画されます。ここで描画したピクセル情報は反射効果を表示するために一時的なデータとして利用します。

テキストの高さを取得

プレビュー文字のすぐ下に反射効果を表示したいので、getColorBoundsRect()メソッドを使って正確にテキストの高さを調べます。

var textHeight:int = bmd.getColorBoundsRect(0xffffffff, 0xffffffff, false).bottom;

例えば、Gとgでは文字の下端が異なります。そこで、getColorBoundsRect()メソッドで白色(0xffffffff)を含まない範囲を取得します。その範囲の下端(bottom)の値をtextHeight変数に格納しています。TextFieldクラスのtextHeightプロパティでは、ここまで細かい高さを取得することはできません。

反射の描画

準備は完了しました。文字のすぐ下に上下反転させながら描画していきます。

// 映り込みを描画していく
for(var i:int = 0; i < textHeight; i++) {
    var multiplier:uint = Math.max(0, i/ textHeight * 64);
    bmd.merge(bmd, new Rectangle(0, i, preview.width, 1), 
        new Point(0, textHeight * 2 - i + 2), 
        multiplier, multiplier, multiplier, 256);
}

for文を利用して、上から1ラインずつ転送しています。文字の下ほど濃く、上に行くほど薄く表示されるように、multiplierの値を調整しています。

merge()は、コピー元のピクセルとコピー先のピクセルを合成するメソッドです。

merge(
    sourceBitmapData:BitmapData,  // 転送元の BitmapData
    sourceRect:Rectangle,         // 転送元の範囲
    destPoint:Point,              // 転送先の座標
    redMultiplier:uint,           // 赤チャンネルに乗算する数値
    greenMultiplier:uint,         // 緑チャンネルに乗算する数値
    blueMultiplier:uint,          // 青チャンネルに乗算する数値
    alphaMultiplier:uint          // αチャンネルに乗算する数値
  ):void

redMultiplier~alphaMultiplierのパラメータは、RGBと透明度のそれぞれのチャンネルごとに、転送元と転送先のどちらの色を強く出すかを0~256の範囲で指定します。

反射効果が描画できたら、上半分のプレビュー表示をそのまま転写したデータは不要です。fillRect()メソッドを利用して白色で塗りつぶしておきます。

// 反射以外を削除
bmd.fillRect(new Rectangle(0, 0, preview.width, textHeight), 0xffffffff);

消さずに残しておいてもよさそうなのですが、実はBitmapDataに文字列を描画すると元の文字列よりもギザギザに描画されてしまいます。ギザギザが残っていると汚いので消しておきます。

メモリへの配慮

あとはBitmapDataを表示するだけですが、その前に、前回のupdateReflection()呼び出しで表示したBitmapDataを破棄しておきます。

if(reflection.bitmapData) {
    reflection.bitmapData.dispose();
}

基本的に、ActionScript 3.0では不要になったメモリはガーベッジコレクタが廃棄してくれます。ただ、BitmapDataオブジェクトは消費するメモリ量が多いので、手動でメモリを解放するdispose()メソッドが用意されています。

BitmapDataの表示

前回のメモリも廃棄できたところで、今回作成したBitmapDataオブジェクトを表示します。

reflection.bitmapData = bmd;

BitmapオブジェクトのbitmapDataプロパティに指定すれば、Bitmapオブジェクトの中身としてBitmapDataオブジェクトの内容が表示されます。

まとめ

今回はジェネレータのソースコードを詳しく流れを見てきました。今までのサンプルに比べて、だいぶ複雑な構成になっています。コードの分量は多いですが、新しい文法は登場していません。この連載を通じて解説した内容が理解できていれば、基本的なプログラミングには困らないと思います。

今後は、皆さんの力で、Flex 3リファレンスガイドを見たり、分からない内容を検索したりしながら、色々なクラスの使い方をマスターしていってくださることを期待しています。

今回でActionScript 3.0の解説は終わりです。次回は、Flex 3フレームワークについて簡単にご説明します。

おすすめ記事

記事・ニュース一覧