Category Archives: Flash

FlashからOAuth用のポップアップ開くメモ

ポップアップしたウインドウ(子)とFlashがのっかってる元のウインドウ(親)はLocalConnection経由でやりとりする。

流れとしては、

元FlashでOAuthするボタンを押す
 ↓
子ウインドウがポップアップする
 ↓
TwitterのOAuthページが表示される。
 ↓
OAuth完了する
 ↓
元Flashに完了した旨が通知される。
 ↓
ポップアップウインドウが閉じる
 ↓
Twitterに各種情報をとりに行く

という感じ。

ここで問題になるのがLocalConnectionをする時に同じConnect IDを使い回すと、同じページを開いた場合にconnect対象が複数になってしまい接続が失敗する。

これを回避するためにconnect用のIDにタイムスタンプを使う。

var connectID:String = String((new Date()).getTime());

Flashから子ウインドウを開く時に、

ExternalInterface.call('window.open("' + url + '", "' + connectID + '")');

もしくは、

navigateToURL(new URLRequest(url) , connectID);

のようにwindow.nameとして生成したIDを子ウインドウに渡す。

子ウインドウはtwitterのOAuth画面が開き、
リダイレクトでもどってきたページでwindow.nameを参照し、
flashvars経由でsend用のswf(通知側)にこのIDを渡す。
send用のswfはこのIDを使ってsendしOAuthが終了したことを親ウインドウに知らせる。

connectが完了したら子ウインドウは自分でwindow.closeして閉じる。

さらに問題になるのがブラウザごとのポップアップブロックの条件。
IEはnavigateToURLがブロックされ、
MacのSafariはポップアップしたページに

タグがあるとwindow.closeできなくなるので、
ExternalInterfaceからwindow.openを呼びウインドウを開く。

ExternalInterface.call('window.open("' + url + '", "' + connectID + '")');

それ以外のブラウザはnavigateToURLで開く。

さらにさらに、Firefox4でwindow.nameを指定してウインドウを開くと、
window.closeした時に他のタブ(親ページ)も巻き込んで閉じてしまうので、
connectIDの頭に”_blank”をつけて開く。

var connectID:String = "_blank" + connectID;

こんな感じ。

Flash、wmode=gpuのバグ?

最近Flashのサイトを制作した際にハマったバグを共有しておきます。

バグの内容は、FireFox、ChromeでFlashサイトを開くと画面が点滅するというものです。
今の段階の結論を言うと、swfをエンベットする時にwmodeをgpu以外のものにするとこの現象は起こらなくなります。

この現象はブラウザ、OS、Flash Playerのバージョンが同じ環境でも異なる機種のMacでは起こらなかったりとハードウェアに依存する部分もあるようです。

検証では以下の環境で現象が確認できました。

黒く点滅し続ける

Mac 10.6.7
Firefox 3.6.17
FlashPlayer 10 debug

Mac 10.4.11
Firefox 3.6.17
FlashPlayer 10.2

画面が真っ白になる

Mac 10.6.7
Firefox 3.6.17
FlashPlayer 10.1

Mac 10.6.7
Firefox 3.5.17
FlashPlayer 10.1.102.64

読み込み時一瞬黒くなる

Windows7
Chrome 11
FlashPlayer 10.2.159.1 debug

Windows7
Chrome 11
FlashPlayer 10.3.181.14 debug

これらの現象が発生する共通する条件は以下の2点です。
・wmodeがgpu
・Flash Playerの設定内「ハードウェアアクセラレーションを有効化」にチェックが入っている
※この項目はデフォルトでチェックが入っています。

また、Flashのパブリッシュ設定内の「ハードウェアアクセラレーション」に関してはこの現象に関係ないようです。

より詳しい情報がわかれば教えて頂けると助かります。
よろしくお願いします。

flex sdkのエラーメッセージを日本語化する

FlashDevelopをアップデートしたら同梱flex sdkのエラーが英語になっちゃったのでメモ。

flex sdkフォルダ内binjvm.configの
java.args=-Xmx384m -Dsun.io.useCanonCaches=false

java.args=-Xmx384m -Dsun.io.useCanonCaches=false -Duser.language=en -Duser.region=JP
に変更する。

FlashDevelop同梱flex sdkのありかは、
C:Program Files (x86)FlashDevelopToolsflexsdkbinjvm.config
これを書き換えて再起動。

参考:

  • ActionScriptによるWebの3Dグラフィックス再入門(1/4):CodeZine
  • swfのtraceをブラウザのコンソールに出力

    firebugやchrome、safariのjavascriptコンソールにswfからtrace感覚で出力したい→できた。

    import flash.external.ExternalInterface;
    
    function log(...args:*):void
    {
        ExternalInterface.call("function(str){ if (typeof window.console == 'object'){ console.log(str) } }", args);
    }
    

    当然ながらhtmlに貼り付けたswfからのみ対応。あとconsoleの有無を判定してから出力してるのでIEでもエラーは出ないはず。trace結果も出ないけど。

    using embedded font in generated text field

    ライブラリパネルで作ったフォントをasで生成したTextFieldに反映する方法メモ。ここで欲しいのはTextFormatのfontに受け渡すフォント名。ライブラリで設定したリンケージ名からフォント名をしょっ引く関数。

    こんな感じ。

    function getFontName( linkageName ):String
    {
           Font( new ( getDefinitionByName( linkageName ) as Class )() ).fontName;
    }
    

    使う時はこんな感じ

    var tf:TextField = new TextField();
    tf.embedFonts = true;
    tf.text = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    
    var fmt:TextFormat = new TextFormat();
    fmt.font = getFontName( "myFont" );
    
    tf.setTextFormat(fmt);
    
    addChild(tf);
    

    パッケージ化したらこんな感じ

    package net.sourcelab.utils
    {
    	import flash.text.Font;
    	import flash.utils.getDefinitionByName;
    	
    	/**
    	 * ...
    	 * @author Bouze
    	 */
    	public class FontUtil 
    	{
    		/**
    		 * フォントのリンケージ名から内部参照用のフォント名を取得する
    		 * @param	linkageName	リンケージ名
    		 * @return	フォント名
    		 */
    		public static function getFontName( linkageName:String ):String
    		{
    			return getFont( linkageName ).fontName;
    		}
    		
    		/**
    		 * フォントのリンケージ名からFontオブジェクトを取得する
    		 * @param	linkageName	リンケージ名
    		 * @return	Fontオブジェクト
    		 */
    		public static function getFont( linkageName:String ):Font
    		{
    			return Font( new ( getDefinitionByName( linkageName ) as Class )() );
    		}
    	}
    }
    

    to do something syncronizing sound

    Flashで曲の再生に正確に同期してごにょごにょしたい時のメモ。Soundboothを使ってマーカーを埋め込むと曲の再生時間に紐づいてイベントが発行されるのでタイミングのズレがなく同期できる。

    まずSoundboothにてキューイベントを発行したい場所にマーカーを配置。

    マーカーパネルで名前、パラメータを編集し、マーカーのタイプをイベントにする(←これ重要)。

    マーカーの配置が完了したら別名で保存から「FLV | F4V」形式で書き出す。書き出し設定のビデオを書き出しのチェックを外して、マルチプレクサタブのFLVにチェックを入れる。オーディオタブでオーディオの設定をして「OK」で書き出し。

    通常通りflvを読み込み、onCuePointにハンドラを張る。onCuePointは再生箇所がSoundboothで設定したマーカーの地点まで来た時に自動で呼び出される。引数に詳細情報を含んだObjectが渡される。

    var nc:NetConnection = new NetConnection();
    nc.connect(null);
    ns = new NetStream(nc);
    var video:Video = new Video();
    video.attachNetStream(ns);
    
    var myClient:Object = new Object();
    myClient.onMetaData = function(info:Object):void
    {
    }
    
    //ここでonCuePointを受け取る
    myClient.onCuePoint = function(info:Object):void
    {
           trace("name:"+info.name);
           trace("time:"+info.time);
           trace("parameter:"+info.parameters.tick);
    };
    ns.client = myClient;
    ns.play("sound.flv");
    

    詳細情報を含んだObjectのパラメータはnameにマーカーの名前が、timeにマーカーの配置された時間(ミリ秒)が、parametersにSoundboothのマーカーパネルで設定したパラメータが名前と値が対で含まれている。

    このやり方だと外部flvを読み込まなくてはいけないので、サウンドデータをファイルに埋め込んでしまいたい場合はSoundboothのマーカー情報をxmlで書き出し、Flash側でイベントの発行すればいい。

    xmlの書き出しはSoundboothでマーカーを設定した後、「ファイル→書き出し」からできる。このxmlにはマーカーごとに時間と名前パラメータなどonCuePointで受け取れる情報はすべて入っている。

    <?xml version="1.0" encoding="UTF-8" standalone="no" ?>
    <FLVCoreCuePoints Version="1">
    	<CuePoint><Time>1566</Time><Name>0</Name></CuePoint>
    	<CuePoint><Time>1813</Time><Name>1</Name></CuePoint>
    	<CuePoint><Time>2255</Time><Name>2</Name></CuePoint>
    </FLVCoreCuePoints>
    

    あとはFlash側でこのxmlを読み込み、音楽の再生時間を監視し続けながら、マーカーの時間が来たらしかるべき処理をすればいい。

    //ライブラリから音楽データを読み込む
    music = new Music() as Sound;
    //30fpsで現在の再生時間を見にいく
    timer = new Timer(1000 / 30);
    
    //マーカー情報を持ったxmlを読み込みパースする
    loader = new URLLoader();
    loader.addEventListener(Event.COMPLETE, function(e:Event)
    {
           var xml:XML = new XML(loader.data);
           var length:int = xml.CuePoint.length();
           var setting:Vector.<Vector.<int>> = new Vector.<Vector.<int>>(length, true);
    
           for (var i:int = 0; i < length; i++)
           {
                   setting[i] = new Vector.<int>(3, true);
                   setting[i][0] = int(xml.CuePoint[i].Name);
                   setting[i][1] = int(xml.CuePoint[i].Time);
                   //通過済みチェックフラグ
                   setting[i][2] = 0;
           }
    
           //音楽と監視開始
           sc = music.play();
           timer.addEventListener(TimerEvent.TIMER, function(e:TimerEvent):void
           {
                   for (var i:int = 0; i < length; i++)
                   {
                           //再生中の時間がキューポイントの時間を超えて、かつ通過済みでなければ
                           if ( setting[i][1] <= sc.position && !setting[i][2] )
                           {
                                   trace("cue name:" + setting[i][0]);
                                   trace("cue time:" + setting[i][1])
                                   //通過済み
                                   setting[i][2] = 0;
                                   return;
                           }
    
                           if ( sc.position > music.length )
                           {
                                   trace("再生終了")
                           }
                   }
           });
           timer.start();
    );
    loader.load(new URLRequest("cuePoints.xml"));
    

    Multi Dimensional Mirror

    鏡シリーズ第3弾。今回はセル上に区切られた画面内の時間がバラバラに進む鏡。
    これは色々展開できそうなのでもうちょっと探ってみる。

    source laboratory™ || Multi Dimensional Mirror

    [AS3]Eventクラスのサブクラスを作る時の注意点

    先輩に教えていただきました。Eventクラスのサブクラスを作る時は内部処理で参照することがあるからcloneメソッドとtoStringメソッドをオーバーライドしなくちゃいけないらしい。

    という事は以前書いたDynamicEventクラスも修正しなくちゃいけない。

    で、そのオーバーライドしたメソッドの中身はイナヅマtvログさんによると、こんな感じでいいらしい。

    public override function clone():Event
    {
    return new DynamicEvent( type, bubbles, cancelable, [arguments] );
    }
    public override function toString():String
    {
    return formatToString( "DynamicEvent", "type", "bubbles", "cancelable", "eventPhase", [arguments] );
    }
    

    参考:

    イナヅマtvログ » EventサブクラスにtoString,cloneを追加しなければいけない?
    Adobe Flex 3 ヘルプ

    [AS3]他のオブジェクトに自由に値を送れるカスタムEvent

    Event通知元から何かしらのメッセージを自由にリスナーオブジェクトに送信するためのメモ。

    Dynamic化したカスタムEvent、DynamicEventを作り、dispatchEventでEventを送信する前にDynamicEventのインスタンスに適当にプロパティをぶっ込んでそのままdispatchEventで送ってやればOK。

    これでEvent受信元は、引数で渡されるDynamicEventのさっき作ったプロパティから値が取り出せる。

    DynamicEventクラスはこんな感じ。Eventクラス継承してDynamic付加してるだけ。

    package
    {
    import flash.events.Event;
    public dynamic class DynamicEvent extends Event
    {
    public static const SOMETHING:String = "something";
    public function DynamicEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false):void
    {
    super( type, bubbles, cancelable );
    }
    }
    }
    

    送信元の処理はこんな感じ。

    var event:DynamicEvent = new DynamicEvent( DynamicEvent.SOMETHING );
    //適当なプロパティをこしらえる
    event.message = "クソ眠い";
    //Event発行
    dispatchEvent( event );
    

    Eventオブジェクトを受け取るイベントハンドラはこんな感じ。

    hoge.addEventListener( DynamicEvent.SOMETHING, function( e:DynamicEvent )
    {
    //「クソ眠い」が出力される。
    trace( e.message );
    });

    #追記
    @lalalilaさんに教えてもらったDataEventを使えばString型のデータなら同様の事が可能っぽい。lalalilaさんありがとうございます。

    #さらに追記
    Eventクラスのサブクラスについてもひとつ注意点が。
    [AS3]Eventクラスのサブクラスを作る時の注意点 | source-lab. note

    Cubic Mirror

    前回からの続き。
    Cubic Mirror
    Cubic Mirror

    要ウェブカメラ、要FlashPlayer10以上

    ※追記
    FlashPlayerのセキュリティ上、ウェブカメラを使おうとするとアクセス許可を求められるけど、このコンテンツ自体はウェブカメラの映像をどこかに流したりというような悪用はできないのでウェブカメラへのアクセスは許可しても大丈夫です。