株式会社ライブキャストロゴ 株式会社ライブキャスト

flashcast:フリーで働くITエンジニア集団のブログ: Macで動くAIRアプリのカスタマイズしたDockアイコンを回転させる方法 〜概要編〜の続きです。今回はDockアイコンを回転させるアニメーションの実装です。

ソースはこちら。

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication
 xmlns:mx="http://www.adobe.com/2006/mxml"
 layout="horizontal" creationComplete="onInit()">
 <mx:Script>
  <![CDATA[
   import mx.core.BitmapAsset;
   [Embed(source="assets/AIRApp_icon128.png")]
   private var icon128:Class;

   [Embed(source="assets/AIRApp_icon16.png")]
   private var icon16:Class;

   private var isAnimation:Boolean = false;
   private var degrees:Number = 0;

   private function onInit():void {
    if (NativeApplication.supportsDockIcon) {
     setIcon((new icon128() as BitmapAsset).bitmapData);
    }
    else if (NativeApplication.supportsSystemTrayIcon) {
     setIcon((new icon16() as BitmapAsset).bitmapData);
    }
    else {
     NativeApplication.nativeApplication.exit();
    }
   }

   private function setIcon(bitmap:BitmapData):void {
    NativeApplication.nativeApplication.icon.bitmaps = [bitmap];
   }

   private function onClick(event:MouseEvent):void {
    if (isAnimation) {
     this.removeEventListener(Event.ENTER_FRAME, onAnimation);
     btnAnimation.label = "Animation";
    }
    else {
     this.addEventListener(Event.ENTER_FRAME, onAnimation);
     btnAnimation.label = "Stop";
    }

    isAnimation = !isAnimation;
   }

   private function onAnimation(event:Event):void {
    var iconBitmap:BitmapData;
    var newIconBitmap:BitmapData;
    var matrix:Matrix;
    var rectangle:Rectangle;

    degrees += 15;

    if (NativeApplication.supportsDockIcon) {
     iconBitmap = new BitmapData(128, 128, true, 0xFFFFFF);
     newIconBitmap = (new icon128() as BitmapAsset).bitmapData;
     rectangle = new Rectangle(0, 0, 128, 128);
    }
    else if (NativeApplication.supportsSystemTrayIcon) {
     iconBitmap = new BitmapData(16, 16, true, 0xFFFFFF);
     newIconBitmap = (new icon16() as BitmapAsset).bitmapData;
     rectangle = new Rectangle(0, 0, 16, 16);
    }

    setIcon(rotateBitmap(iconBitmap, newIconBitmap,
     iconBitmap.width/2, iconBitmap.height/2, rectangle, degrees));

    if (degrees >== 360) {
     degrees = degrees - 360;
    }
   }

   private function rotateBitmap(iconBitmap:BitmapData, newIconBitmap:BitmapData,
    originX:int, originY:int, rectangle:Rectangle, degrees:int):BitmapData {
    var radian:Number = degrees/180 * Math.PI;
    var matrix:Matrix = new Matrix();
    matrix.rotate(radian);
    matrix.translate(originX-Math.cos(radian)*originX+Math.sin(radian)*originY,
     originY-Math.cos(radian)*originX-Math.sin(radian)*originY);
    iconBitmap.draw(newIconBitmap, matrix, null, null, rectangle);

    return iconBitmap;
   }
  ]]>
 </mx:Script>
 <mx:Button label="Animation" id="btnAnimation" click="onClick(event)"/>
</mx:WindowedApplication>

アプリ起動時の画面は[Animation]ボタンがついているだけの単純な画面です。

[Animation]ボタンのクリックイベントハンドラでは、isAnimationフラグでDockアイコンがアニメーション中か否かを制御しています。アニメーション中でなければenterFrameのイベントハンドラを登録し、ボタンのラベルを[Stop]に変更します。アニメーション中であれば、enterFrameのイベントハンドラを解除し、ボタンのラベルを[Animation]に戻します。

private function onClick(event:MouseEvent):void {
 if (isAnimation) {
  this.removeEventListener(Event.ENTER_FRAME, onAnimation);
  btnAnimation.label = "Animation";
 }
 else {
  this.addEventListener(Event.ENTER_FRAME, onAnimation);
  btnAnimation.label = "Stop";
 }

 isAnimation = !isAnimation;
}

enterFrameイベントが発生すると、onAnimation関数がコールされます。onAnimation関数では画像を回転する処理を行っています。このサンプルでは、毎フレーム15度ずつ回転角を増加させて基準となる画像を回転させています。

前回変形した画像をさらに回転させているわけではなく、毎回基準となる画像に対して処理をしているわけです(この理由については後で説明します)。

degrees += 15;

setIcon関数には、変形元の画像、合成先の画像、基準点、描画領域、回転角などを渡し、ラジアンを求めて、

var radian:Number = degrees/180 * Math.PI;

Matrixオブジェクトを生成し、rotateメソッドで回転時の変換行列を設定します。

var matrix:Matrix = new Matrix();
matrix.rotate(radian);

回転させると画像の中心点の座標が移動しますので、translateメソッドで元の位置に戻します。

matrix.translate(originX-Math.cos(radian)*originX+Math.sin(radian)*originY,
 originY-Math.cos(radian)*originX-Math.sin(radian)*originY);

中心点を平行移動させる値の計算式は以下の通り。

tx=x-ax-cy=64-cos(q)*64-(-sin(q))*64
ty=y-bx-dy=64-sin(q)*64-cos(q)*64

※qの単位はラジアン。詳細はflashcast:フリーで働くITエンジニア集団のブログ: Macで動くAIRアプリのカスタマイズしたDockアイコンを回転させる方法 〜概要編〜をご参考に。

cos(q)、sin(q)の結果は小数になることもありますので、回転角度によっては誤差が生じます。そうした場合、その加工した画像に対して更に回転をかけることを繰り返すと、画像が崩れていくことがあるのです。これが、変形した画像をさらに回転させず、基準となる画像を1つとし、それに対して処理をしている理由になります。

最後に、回転と平行移動の設定をしたMatrixオブジェクトを利用して画像を加工し、Dockアイコンに設定します。

これを繰り返すと、、、

と言う感じでアニメーションしてるように見えるんです。

ちなみに、今回のサンプルソースでもWindowsのタスクトレイアイコンの回転を実装しています。タスクトレイをサポートしているプラットフォームか判断し、基準となる画像と合成する画像サイズを変更している以外は、Dockアイコンと同じ方法でタスクトレイのアイコンも回転させることが出来ます。

■関連記事へのリンク
flashcast:フリーで働くITエンジニア集団のブログ: Macで動くAIRアプリのDockアイコンをカスタマイズする方法
flashcast:フリーで働くITエンジニア集団のブログ: Macで動くAIRアプリのカスタマイズしたDockアイコンをアニメーションさせる方法
flashcast:フリーで働くITエンジニア集団のブログ: Macで動くAIRアプリのカスタマイズしたDockアイコンを回転させる方法 〜概要編〜
flashcast:フリーで働くITエンジニア集団のブログ: Macで動くAIRアプリのカスタマイズしたDockアイコンを縮小/拡大させる方法(2009/2/4追記)