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

前回、透明度設定をサポートしているOSとサポートしていないOSがあることをご紹介しましたが、今回は透明度設定がサポートされているOS上で、実際に半透明の円を描画してみたいと思います。

まず、透明のデスクトップウィンドウを作成します。
・NativeWindowを継承した自前のクラスをつくります。
※ここではTransWindowというクラス名にしています。
・TransWindowクラスのコンストラクタでウィンドウの基本設定をします。
※システムクロムを非表示にし、透明設定をします。
※ここまでは前回の記事のソースと実装の仕方は違いますがやっていることは同じです。

次にSpriteクラスを使用して、黒色半透明の円を描画します。
・塗りを黒、アルファ値を0.5に設定します。
・半径150pxの円を描画します。
・透明デスクトップウィンドウのStageオブジェクトに、黒色半透明の円を配置します。

ブラウザ (Flash® Player) で実行されている SWF コンテンツの場合、Stage は Flash コンテンツが表示されている全体の領域を表します。AIR で実行されているコンテンツの場合、各 NativeWindow オブジェクトが、対応する Stage オブジェクトを持ちます。

Stage – ActionScript 3.0 言語およびコンポーネントリファレンスより引用。

package
{
 import flash.display.Graphics;
 import flash.display.NativeWindow;
 import flash.display.NativeWindowInitOptions;
 import flash.display.NativeWindowSystemChrome;
 import flash.display.NativeWindowType;
 import flash.display.Sprite;
 import flash.display.StageAlign;
 import flash.display.StageScaleMode;

 public class TransWindow extends NativeWindow
 {
  public function TransWindow()
  {
   var options:NativeWindowInitOptions = new NativeWindowInitOptions();

   options.systemChrome = NativeWindowSystemChrome.NONE;
   options.type = NativeWindowType.LIGHTWEIGHT;
   options.transparent = true;
   options.maximizable = true;

   super(options);
  }

  public function initWindow():void {
   var background:Sprite = new Sprite();
   var graphics:Graphics = background.graphics;
   graphics.beginFill(0x000000, 0.5);
   graphics.drawCircle(500, 500, 150);
   graphics.endFill();
   stage.addChild(background);
   stage.align = StageAlign.TOP_LEFT;
   stage.scaleMode = StageScaleMode.NO_SCALE;
   this.maximize();
  }
 }
}

このTransWindowを他の画面から開きます。開く画面のソースはこちら。

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
 layout="horizontal" horizontalAlign="center" verticalAlign="middle">
 <mx:Button label="Open" id="btnOpen" click="onOpenClick(event)"/>
 <mx:Button label="Exit" id="btnExit" click="onExitClick(event)"/>
 <mx:Script>
  <![CDATA[
   import mx.core.UIComponent;

   private function onOpenClick(event:MouseEvent):void {
    var win:TransWindow = new TransWindow();
    win.initWindow();
    win.activate();
   }

   private function onExitClick(event:MouseEvent):void {
    NativeApplication.nativeApplication.exit();
   }
  ]]>
 </mx:Script>
</mx:WindowedApplication>

実行結果は以下のようになります。
・Windowsの場合

・Macの場合

次に、TransWindowクラスに以下の処理を追加します。黒色半透明の円上にマウスオーバーしたのをトリガーに、黒色半透明の円を徐々に透明にしていき最終的に見えなくするという処理です。実装レベルでは以下のようなロジックになります。

・黒色半透明の円にMouseOverイベントハンドラを登録する。
・MouseOverイベントハンドラでは、円のアルファ値を初期値に戻す。
※徐々に透明になっていく円をくっきり表示に戻すためです。
・MouseOverイベントハンドラではEnterFrameイベントハンドラを登録する。
※MouseOverの度にEnterFrameイベントハンドラが登録されてしまうと削除漏れにつながり、メモリリークの要因となりかねませんのでhasEventListenerでチェックし初回のみ登録するようにします。
・EnterFrameイベントハンドラでは、フレーム毎に黒色半透明の円を徐々に透明にしていく。
・EnterFrameイベントハンドラでは、黒色半透明の円が完全に透明になったらEnterFrameイベントハンドラを削除する。

package
{
 import flash.display.Graphics;
 import flash.display.NativeWindow;
 import flash.display.NativeWindowInitOptions;
 import flash.display.NativeWindowSystemChrome;
 import flash.display.NativeWindowType;
 import flash.display.Sprite;
 import flash.display.StageAlign;
 import flash.display.StageScaleMode;
 import flash.events.Event;
 import flash.events.MouseEvent;

 public class TransWindow extends NativeWindow
 {
  private var counter:Number = 0;

  public function TransWindow()
  {
   var options:NativeWindowInitOptions = new NativeWindowInitOptions();

   options.systemChrome = NativeWindowSystemChrome.NONE;
   options.type = NativeWindowType.LIGHTWEIGHT;
   options.transparent = true;
   options.maximizable = true;

   super(options);
  }

  public function initWindow():void {
   var background:Sprite = new Sprite();
   var graphics:Graphics = background.graphics;
   graphics.beginFill(0x000000, 0.5);
   graphics.drawCircle(500, 500, 150);
   graphics.endFill();
   background.addEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
   stage.addChild(background);
   stage.align = StageAlign.TOP_LEFT;
   stage.scaleMode = StageScaleMode.NO_SCALE;
   this.maximize();
  }

  private function onMouseOver(event:MouseEvent):void {
   var background:Sprite = event.target as Sprite;
   counter = 1;
   background.alpha = counter;
   if (!background.hasEventListener(Event.ENTER_FRAME)) {
    background.addEventListener(Event.ENTER_FRAME, onEnterFrame);
   }
  }

  private function onEnterFrame(event:Event):void {
   counter -= 0.01

   var background:Sprite = event.target as Sprite;
   background.alpha = counter;

   if (counter <= 0) {
    background.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
   }
  }
 }
}

実行結果は以下のようになります。
・Windowsの場合

・Macの場合

このテストをしていてWindowsとMacでMouseOverイベントに対する挙動が異なることに気づきました。

円が完全に透明になった時に、Windows Vistaでは、MouseOverイベントが発生しないのに対して、MacではMouseOverイベントが発生するのです。

ウィンドウが作成された後で、transparent プロパティを変更することはできません。透明度は、ウィンドウの視覚的な外観とマウス動作に影響します。ピクセルのアルファ値が特定のしきい値 (オペレーティングシステムによって約 0.06 〜 0.01) を下回っている場合、ウィンドウはそのピクセル上のマウスイベントをキャプチャしません。

NativeWindow – ActionScript 3.0 言語およびコンポーネントリファレンスより引用。

MouseOverイベントハンドラの先頭に処理を追加して、円のalpha値を確かめてみます。

private function onMouseOver(event:MouseEvent):void {
 var background:Sprite = event.target as Sprite;
 Alert.show(background.alpha.toString());
 counter = 1.0;
 background.alpha = counter;
 if (!background.hasEventListener(Event.ENTER_FRAME)) {
  background.addEventListener(Event.ENTER_FRAME, onEnterFrame);
 }
}

あれ?0だ。
Macでは、アルファ値が0でもMouseEventが発生してしまうようです。ちなみに、今回のサンプルソースでは、円のアルファ値が0になったら、MouseOverイベントハンドラを削除してやることで対処できます。

private function onEnterFrame(event:Event):void {
 counter -= 0.01

 var background:Sprite = event.target as Sprite;
 background.alpha = counter;

 if (counter <= 0) {
  background.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
  background.removeEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
 }
}

でも、これってバグぽい気がします。

■関連記事へのリンク
flashcast:フリーで働くITエンジニア集団のブログ: クロスプラットフォームなAIRアプリ開発時の注意点 〜Dockアイコンやタスクトレイアイコンについて〜
flashcast:フリーで働くITエンジニア集団のブログ: クロスプラットフォームなAIRアプリ開発時の注意点 〜NativeWindowの透明度設定について〜