— boreal-kiss.com

Archive
2008年5月 Monthly archive

元ネタはFlash Player 10 Beta: 3D(注:動画)。これ中々面白いからFlash Player 10を使わずに作って展示したいんだけど、z軸方向をどう扱うのかが問題。普通にリサイズすれば同じ動きになるんだろうか。クリックで各画像にフォーカス、ダブルクリックでその画像のPermalinkへ飛びます。

[FLASH10]Tumblr Spiral
[FLASH10]Tumblr Spiral (要Flash Plaeyer 10)

以下ソース。テキストフィールド等の装飾品は除いています。

Main.as (メインクラス)

package {
    /*
    * Requires Flex SDK 3.0.1 or later to be compiled
    */
    import caurina.transitions.Tweener;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.net.URLLoader;
    import flash.net.URLRequest;

    [SWF(width="550",height="400",backgroundColor="0x0",frameRate="24")]

    public class Main extends Sprite{
        //Setting for Tumblr API
        private const IMG_SIZE:int = 75;
        private const USERID:String = "borealkiss";
        private const QUERY_NUM:int = 30;
        
        //Canvas
        private var container:Sprite;
        
        public function Main(){         
            container = new Sprite();
            container.x = stage.stageWidth/2;
            container.y = stage.stageHeight/2;
            addChild(container);
            
            //Query string for Tumblr API
            var str:String = "http://"+USERID+".tumblr.com/api/read?num="
                                +QUERY_NUM+"&type=photo";
            var loader:URLLoader = new URLLoader();
            loader.load(new URLRequest(str));
            loader.addEventListener(Event.COMPLETE,parseXML);
        }
        
        //XML parser
        public function parseXML(e:Event):void{
            var xmlData:XML = new XML(e.target.data);
            var imgURLs:Array = new Array();
            var postURLs:Array = new Array();
            
            for each (var ch1:XML in xmlData.posts.post){
                var postURL:String = ch1.attribute("url").toString();
                postURLs.push(postURL);
                for each (var ch2:XML in ch1.child("photo-url")){
                    if (ch2.attribute("max-width") == IMG_SIZE){
                        var imgURL:String = ch2.toString();
                        imgURLs.push(imgURL);
                    }
                }
            }
            makeFaces(imgURLs,postURLs,true);
        }
        
        private function makeFaces(aryImgURLs:Array,aryPostURLs:Array,
                                    flgNavigate:Boolean):void{
            
            //Radius of a spiral
            const RAD:Number = 250;
            
            for (var i:uint=0; i<aryImgURLs.length; i++){
                var f:FaceDoubleClick = new FaceDoubleClick(
                            aryImgURLs[i],aryPostURLs[i],flgNavigate);
                
                //Set photos along the spiral
                f.x = RAD*Math.cos(i*Math.PI/6);
                f.y = RAD*Math.sin(i*Math.PI/6);
                f.z = (aryImgURLs.length-i)*200;
                f.buttonMode = true;
                f.addEventListener(MouseEvent.CLICK,onClick);
                container.addChild(f);
            } 
        }
        
        private function onClick(e:MouseEvent):void{
            //Must be tuned upon image size
            var offsetX:Number = 270;
            var offsetY:Number = 190;
            var offsetZ:Number = 0;
            
            var sp:Sprite = Sprite(e.target);
            Tweener.addTween(container,{x:-sp.x+offsetX, y:-sp.y+offsetY, 
                                        z:-sp.z+offsetZ, time:1});
        }
    }
}

FaceDoubleClick.as

package{
    import flash.display.Bitmap;
    import flash.display.Loader;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.net.URLRequest;
    import flash.net.navigateToURL;
    import flash.system.LoaderContext;
    
    public class FaceDoubleClick extends Sprite{
        private var _url:String;
        private var _loader:Loader;
        private var flgNavigate:Boolean;
        
        public function FaceDoubleClick(imgURL:String,postURL:String = "null",
                                                flgNavigate:Boolean = false){
            _url = postURL;
            this.flgNavigate = flgNavigate;

            var context:LoaderContext = new LoaderContext(true);            
            _loader = new Loader();   
            _loader.load(new URLRequest(imgURL), context);
            _loader.contentLoaderInfo.addEventListener(Event.INIT,init);
            
            this.doubleClickEnabled = true;
            addEventListener(MouseEvent.DOUBLE_CLICK, doubleClick);
        }
        
        private function init(e:Event):void{
            //The image center will be placed on center
            var bmp:Bitmap = Bitmap(e.target.content);
            bmp.x = -bmp.width/2;
            bmp.y = -bmp.height/2;
            addChild(bmp);
        }
        
        private function doubleClick(e:MouseEvent):void{
            if (flgNavigate){
                navigateToURL(new URLRequest(_url));
            }
        }  
    }
}
Read More
  1. Flash Player 10 単体版をこちらからダウンロード。現在のところオフィシャルには配布されていない様子。

    [追記:2008/06/01] 3/16以降のFlex SDK Nightly Buildに含まれているようです(akihiro kamijo: Flash Player 10 ベータのデバッグプレーヤ)
  2. SWFファイルを(常時)このプレイヤーで開くように関連づける。Macの場合swfファイルを右クリック > 情報を見る > このアプリケーションで開く > Flash Player 10を選択
  3. 実際にFlex Builder 3でActionScriptプロジェクトを作成
  4. 作成したプロジェクトのプロパティ > 実行/デバッグの設定 > 現在のプロジェクトを選択して編集(なければ新規作成)
  5. 起動するURLまたはパス > 「デフォルトを使用する」のチェックをはずす
  6. デバッグの項の起動パスをswfファイルそのものに変更(bin-debug/****.swf)して設定適用
  7. 実際にデバッグを起動すると「現在のバージョンでデバッグを試みますか?」と聞かれるので「はい」を選択

これでswfファイルを起動するFlash Playerが先ほどダンロードしたFlash Player 10に設定されているならデバッグが機能するはず。情報元はTargeting Flash Player 10 Beta with Flex SDK 3.0.x – Flex SDK – Confluenceのコメント欄より。

Read More

円環状の多面体(長方形が複数繋がったようなやつ。各長方形は三角形二つで構成)に沿ってパノラマ画像を貼付け。胆はテクスチャ貼付け用のUVT座標の設定と、円環の内側が見えるようcullingしたこと。z-sortはなんにもしてない。あと、今回新しく追加されたVector3DクラスをPointクラスの3Dバージョン(x,y,z)ではないかと推測して使っているので正しい使い方ではないかも。

[追記2008/05/19]
Vector3Dの説明やプリミティブ図形の描画方法を説明している記事発見。かなり参考になる。

[FLASH10]パノラマ
[FLASH10]パノラマ (要Flash Plaeyer 10)

package {
    /*
    * Requires Flex SDK 3.0.1 or later to be compiled
    */
    import __AS3__.vec.Vector;
    import flash.display.Bitmap;
    import flash.display.Sprite;
    import flash.display.TriangleCulling;
    import flash.events.Event;
    import flash.geom.Vector3D;
    
    [SWF(width="700",height="400",backgroundColor="0x0",frameRate="24")]

    public class Astro11 extends Sprite{
        [Embed(source="img/panoramic.jpg")]
        private var TestImage:Class;
        
        private var img:Bitmap;
        private var container:Sprite;
        
        public function Astro11(){
            //Canvas
            container = new Sprite();
            container.x = stage.stageWidth/2;
            container.y = stage.stageHeight/2;
            addChild(container);
            
            img = new TestImage();
            addEventListener(Event.ENTER_FRAME,rotate);
        }
        
        private function rotate(e:Event):void{
            const DUMP:Number = 2.5e-3;
            var dx:Number = (mouseX - stage.stageWidth/2);
            drawScene(dx*DUMP);
        }
        
        private function drawScene(angle:Number):void{
            container.graphics.clear();
            
            //Total number of rectangle faces along a torus, 
            //i.e., 2xTOTAL triangles.
            const TOTAL:int = 30;
            
            //Radius of the torus
            const R:Number = 200;
            
            //Focal length
            const F:Number = 200;
            
            //Angular offset of the image 
            //for hiding the image's "non-gapless edge"
            const OFFSET:Number = -Math.PI/2;
            angle += OFFSET;
            
            //Size of a rectangle that two triangles make
            var w:Number = 2*Math.PI*R/TOTAL;
            var h:Number = img.height;
            
            //Arguments for drawRectangles
            var vertices:Vector.<Number> = new Vector.<Number>();
            var indices:Vector.<int> = new Vector.<int>();
            var uvtData:Vector.<Number> = new Vector.<Number>();
            
            for (var i:int=0; i<TOTAL; i++){
                var dt:Number = 2*Math.PI/TOTAL;
                 
                indices.push(2*i,2*(i+1),2*i+1, 2*i+1,2*(i+1),2*(i+1)+1);
                
                if (i==0){
                    //Define the position of each vertex in 3D space
                    var v0:Vector3D = new Vector3D(R*Math.cos(angle+i*dt),
                                                -h/2,R*Math.sin(angle+i*dt));
                    var v1:Vector3D = new Vector3D(R*Math.cos(angle+i*dt),
                                                h/2,R*Math.sin(angle+i*dt));
                    var v2:Vector3D = new Vector3D(R*Math.cos(angle+(i+1)*dt),
                                                -h/2,R*Math.sin(angle+(i+1)*dt));
                    var v3:Vector3D = new Vector3D(R*Math.cos(angle+(i+1)*dt),
                                                h/2,R*Math.sin(angle+(i+1)*dt));
                
                    //Scaling factor
                    var t0:Number = F/(F + v0.z);
                    var t1:Number = F/(F + v1.z);
                    var t2:Number = F/(F + v2.z);
                    var t3:Number = F/(F + v3.z);
                
                    vertices.push(v0.x*t0,v0.y*t0, v1.x*t1,v1.y*t1, 
                                    v2.x*t2,v2.y*t2, v3.x*t3,v3.y*t3);
                    uvtData.push(i/TOTAL,i/TOTAL,t0, i/TOTAL,1,t1, 
                                    (i+1)/TOTAL,0,t2, (i+1)/TOTAL,1,t3);
                }
                else{
                    v2 = new Vector3D(R*Math.cos(angle+(i+1)*dt),
                                                -h/2,R*Math.sin(angle+(i+1)*dt));
                    v3 = new Vector3D(R*Math.cos(angle+(i+1)*dt),
                                                h/2,R*Math.sin(angle+(i+1)*dt));
                    
                    t2 = F/(F + v2.z);
                    t3 = F/(F + v3.z);
                    
                    vertices.push(v2.x*t2,v2.y*t2, v3.x*t3,v3.y*t3);
                    uvtData.push((i+1)/TOTAL,0,t2, (i+1)/TOTAL,1,t3);
                }
            }
            
            //Frontface culling
            container.graphics.beginBitmapFill(img.bitmapData);
            container.graphics.drawTriangles(vertices,indices,uvtData,
                                            TriangleCulling.POSITIVE);
            container.graphics.endFill();
        }
    }
}
Read More

Flash Player 10で新しく使えるようになったグラフィックメソッドのdrawTrianglesを使ってみた。まだ完全に理解してないのだけど、このメソッドでおそらくポリゴン表面に画像貼付けたりできるようになると思う。画像歪ませるのが簡単になった。

[FLASH 10]お尻プルルン
[FLASH 10]お尻プルルン (要Flash Plaeyer 10)

ソース。

package {
    /*
    * Requires Flex SDK 3.0.1 or later to be compiled
    */
    import __AS3__.vec.Vector;
    import flash.display.Bitmap;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Point;

    [SWF(width="550",height="400",backgroundColor="0x0",frameRate="24")]

    public class Astro08 extends Sprite{
        [Embed(source="img/girl.jpg")]
        private var TestImage:Class;
        
        private var img:Bitmap;
        private var container:Sprite;
        
        //true for mouse down, otherwise false.
        private var flgMouseDown:Boolean;
        
        //true for mouse rollover, otherwise false.
        private var flgMouseOver:Boolean;
        
        //drawTriangles
        private var vertices:Vector.<Number>;
        private var indices:Vector.<int>;
        private var uvts:Vector.<Number>;
        
        //The origin of springing motion
        private var initP:Point;
        
        //Velocities of springing motion
        private var velocities:Vector.<Number> = Vector.<Number>([0,0]);
        
        public function Astro08(){
            img = new TestImage();
            initImage(img.width*0.5,img.height*0.5);
            
            container.addEventListener(MouseEvent.MOUSE_DOWN,onMouseDown);
            container.addEventListener(MouseEvent.MOUSE_UP,onMouseUp);
            container.addEventListener(MouseEvent.MOUSE_OVER, onRollOver);
            container.addEventListener(MouseEvent.MOUSE_OUT, onRollOut);
            addEventListener(Event.ENTER_FRAME,onEnterFrame);
        }
        
        private function initImage(w:Number,h:Number):void{
            initP = new Point(w/2,h/2);
            vertices = Vector.<Number>([0,0, w,0, initP.x,initP.y, w,h, 0,h]);
            indices = Vector.<int>([0,1,2, 1,3,2, 2,3,4, 2,4,0]);
            uvts = Vector.<Number>([0,0, 1,0, 1/2,1/2, 1,1, 0,1]);
            
            //Canvas
            container = new Sprite();
            container.x = stage.stageWidth/2 - w/2;
            container.y = stage.stageHeight/2 - h/2;
            addChild(container);
            
            container.graphics.beginBitmapFill(img.bitmapData);
            container.graphics.drawTriangles(vertices,indices,uvts);
        }
        
        private function onEnterFrame(e:Event):void{
            const SPRING:Number = 0.9;
            const FRICTION:Number = 0.8;
            
            container.graphics.clear();
            
            if(flgMouseDown && flgMouseOver){
                vertices[4] = container.mouseX;
                vertices[5] = container.mouseY;
            }
            else{
                //Springing motion
                var dx:Number = initP.x - vertices[4];
                var ax:Number = dx*SPRING;
                velocities[0] += ax;
                velocities[0] *= FRICTION;
                vertices[4] += velocities[0];
                
                var dy:Number = initP.y - vertices[5];
                var ay:Number = dy*SPRING;
                velocities[1] += ay;
                velocities[1] *= FRICTION;
                vertices[5] += velocities[1];
            }
            
            container.graphics.beginBitmapFill(img.bitmapData);
            container.graphics.drawTriangles(vertices,indices,uvts);
        }
        
        private function onMouseDown(e:MouseEvent):void{
            flgMouseDown = true;
        }
        
        private function onMouseUp(e:MouseEvent):void{
            flgMouseDown = false;
        }
        
        private function onRollOver(e:MouseEvent):void{
            flgMouseOver = true;
        }
        
        private function onRollOut(e:MouseEvent):void{
            flgMouseOver = false;
        }
    }
}
Read More

数日前にFlash Player 10(Astro)のベータ版が発表されて新機能や新クラスの話題でFlash界隈は盛り上がってるみたい。DisplayObjectのプロパティに新しくz軸が追加されて3D描画が可能になった。とはいっても今まで自分で設定しなくちゃいけなかった焦点距離の計算が省けただけなのかな?単純にdrawRect()するだけではdepth sortingやbackface cullingまではしてくれないみたい。僕も色々調べ中。とりあえずFlash Player 10 で 3Dしてみた (Unknown Quality)を参考に3Dやってみた。参考元はFlex SDK 3.0.1より前のものを使用しているのでパブリッシュに色々トリック(astro::って部分等)を使ってるみたいだけど、現在はFlash Player 10用にパブリッシュできるFlex SDK 3.0.1.xが配布されているので気にしなくてよい。

[FLASH 10]My First Astro
[FLASH 10]My First Astro (再生にはFlash Plaeyer 10が必要)

[関連リンク]

一応ソースコード。rotationXとかあるよね。

package {
    /*
    * Requires Flex SDK 3.0.1.x to be compiled
    */
    import flash.display.Shape;
    import flash.display.Sprite;
    import flash.events.Event;
    
    [SWF(width="550",height="400",backgroundColor="0x0")]
    
    public class Main extends Sprite{
        
        //size of the box
        private const SIZE:Number = 100;
        
        public function Main(){
            var spr:Sprite = new Sprite();
            spr.x = stage.stageWidth/2;
            spr.y = stage.stageHeight/2;
            addChild(spr);
            makeCube(spr,0x005885);
            spr.addEventListener(Event.ENTER_FRAME,rotate);
        }
        
        private function makeCube(s:Sprite,c:uint):void{
            //front
            var f1:Shape = new Shape();
            f1.graphics.lineStyle(2,c);
            f1.graphics.drawRect(0,0,SIZE,SIZE);
            f1.x = -SIZE/2;
            f1.y = -SIZE/2;
            f1.z = -SIZE/2;
            s.addChild(f1);
            
            //back
            var f2:Shape = new Shape();
            f2.graphics.lineStyle(2,c);
            f2.graphics.drawRect(0,0,SIZE,SIZE);
            f2.x = -SIZE/2;
            f2.y = -SIZE/2;
            f2.z = SIZE/2;
            s.addChild(f2);
            
            //left
            var f3:Shape = new Shape();
            f3.graphics.lineStyle(2,c);
            f3.graphics.drawRect(0,0,SIZE,SIZE);
            f3.rotationY = 90;
            f3.x = -SIZE/2;
            f3.y = -SIZE/2;
            f3.z = SIZE/2;
            s.addChild(f3);
            
            //right
            var f4:Shape = new Shape();
            f4.graphics.lineStyle(2,c);
            f4.graphics.drawRect(0,0,SIZE,SIZE);
            f4.rotationY = 90;
            f4.x = SIZE/2;
            f4.y = -SIZE/2;
            f4.z = SIZE/2;
            s.addChild(f4);
            
            //top
            var f5:Shape = new Shape();
            f5.graphics.lineStyle(2,c);
            f5.graphics.drawRect(0,0,SIZE,SIZE);
            f5.rotationX = 90;
            f5.x = -SIZE/2;
            f5.y = -SIZE/2;
            f5.z = -SIZE/2;
            s.addChild(f5);
            
            //bottom
            var f6:Shape = new Shape();
            f6.graphics.lineStyle(2,c);
            f6.graphics.drawRect(0,0,SIZE,SIZE);
            f6.rotationX = 90;
            f6.x = -SIZE/2;
            f6.y = SIZE/2;
            f6.z = -SIZE/2;
            s.addChild(f6);
        }
        
        private function rotate(e:Event):void{
            const DUMP:Number = 0.25;
            var dx:Number = (mouseX - stage.stageWidth/2);
            var dy:Number = (mouseY - stage.stageHeight/2);
            e.target.rotationY = -dx*DUMP;
            e.target.rotationX = dy*DUMP;
        }
    }
}
Read More

Papervision3D2.0(Great White)の割と最近のドキュメントがアップされてる(2008/04/22)。2.0は現段階でアルファ版でオフィシャルなドキュメンが出るのは当分先っぽかったのでたすかった。早速Fluidで専用ブラウザー化してドックに追加しといた。Fluid大活躍!Great White API

[関連リンク]
public var むらけん:Flasher; ASDoc GrateWhite(PaperVision3D2.0)

Read More

例えばFirefoxで複数のwebページを閲覧する時に、「その中の1ページが死ぬほど重い → Firefox全体が応答しなくなり強制終了 → Firefoxを立ち上げ直し」というのを繰り返すことがあって何かいい方法がないかなーと思ってた。Fluidは、そういう「巻き込み事故」を避けるためにブラウザーを複数のアプリケーションにしてしまうソフト。例えばTwitter専用のブラウザーを簡単に作成できる。

ところでFluidにはサムネイルプラグインというものが標準装備されている。閲覧中のwebページ内のリンク先のスクリーンショットをカバーフロー(iTunesのジャケットギャラリー表示のようなやつ)で表示してくれるものなんだけど、これ名前の通りサムネイルで一括表示するので、例えばはてなブックマークのホットエントリのリンク先をサムネイルで表示したところで文字が小さすぎて読めないためあまり実用的ではない(ホットエントリーのサムネイル表示方法は[N] 「Fluid」のサムネイルプラグインを試したを参照)。文字がでかくてサムネイル表示で十分なwebページ集というとMuxtapeってわけですよ!

Muxtapeはトップにユーザーのmixtapeがランダム表示されるのだけど、これがユーザー名のみの表示でmixtape中身を見るためには一々各ユーザーページを確認しないといけない。ところがMuxtapeのユーザーページは文字が大きくてシンプルな作りをしているのでFluidのサムネイルプラグインで一括表示・チェックするにはもってこいなわけ。僕は今までどんなジャンルのmixtapeかもわからないユーザーのリンク先をクリックしてまで中身を確認したことがなかったので、このFluidの使い方は画期的なんじゃないかなーと思ってる。具体的な設定方法はFluid x Muxtape = すばらしい » www.slack77.netに書いてある(動画付き。ただししゃべり方が鼻につく)。動画元のコメント欄も「Macが欲しい」という感じになってた。またFlickrにはFluidで作成したアプリケーション用のアイコンがたくさん配布されているのでMacユーザーはこの機会に是非Fluidを活用してみてはいかがでしょうか。

MuxtapeをFluidのサムネイルプラグインで表示

[関連リンク]
MOONGIFT: » 専用Webアプリケーションを手軽に「Fluid」:オープンソースを毎日紹介

Read More

FLASHをブログパーツにして配布する場合にはJavaScriptに収めてしまってJSスクリプトを配布する方法がある。しかし自分が今までやっていた方法だとJS経由でFLASHに変数を渡す場合に問題が起こる可能性があることがわかったのでその解決方法について。まず今までの問題のある書き方の場合、例えばJSファイルを以下のように記述していた。

//"http://example.com/showme.js"
function showMe(userid){
    //swfを表示するのに必要な<object>タグ全体。実際は改行無し
    var obj = '<objcet>
            ...略...
            <param name="FlashVars" value="userid='+userid+'" />
            ...略...
            <embed ...略...FlashVars="userid='+userid+'"></embed></object>';
    document.write(obj);
}

配布用ソースをは以下のような書式。

//配布用html貼付けソース
<script type="text/javascript" src="http://example.com/showme.js"></script>
<script type="text/javascript">showMe("borealkiss");</script>

この場合、(万が一)ユーザーがすでにshowMeという関数を他で定義していると関数名がコンフリクトを起こす(以下の例ではユーザーの期待に反しブログパーツが二個表示される)。

<html>
<head>
<!--ユーザー定義のshowMe関数-->
<script type="text/javascript">
function showMe(name){
    alert("Welcome, " + name + "!");
}
</script>
</head>

<body>
<!--ブログパーツのshowMe関数-->
<script type="text/javascript" src="http://example.com/showme.js"></script>
<script type="text/javascript">showMe("borealkiss");</script>

<!--ユーザー定義のshowMe関数を実行させるつもりだったのに!-->
<script type="text/javascript">showMe("borealkiss");</script>
</body>
</html>

このコンフリクトはこちらが定義した関数を匿名関数(無名関数)でラップしてしまい、その場で匿名関数を実行させることで回避できる(special thx: YungSang)。

//"http://example.com/showme.js" 修正版
(function(){
    function showMe(userid){
        var obj = '<objcet>
                ...略...
                <param name="FlashVars" value="userid='+userid+'" />
                ...略...
                <embed ...略...FlashVars="userid='+userid+'"></embed></object>';
        document.write(obj);
    }
    showMe(window._com_example_showMe_userid);
})();

配布ソースは以下のように修正。

//配布用html貼付けソース修正版
<script type="text/javascript">var _com_example_showMe_userid = "borealkiss"</script>
<script type="text/javascript" src="http://example.com/showme.js"></script>

ポイントは匿名関数でラップしてしまうことでブログパーツ表示用関数をローカルスコープにすること;

function(){
    function showMe(...){
        ...
    }
    showMe(***);
}

それと匿名関数をその場で実行させることだ;

(function(){...})();

これでwindow上で定義した変数”_com_example_showMe_userid”がコンフリクトしなければユーザーに迷惑をかけることはないはず。そのためにできるだけユニークな変数名にするとよい(例えば自ドメイン名に由来するものetc.)。

[関連リンク]
JavaScript++かも日記: 【JavaScript】匿名関数で匿名スコープ&インスタンス

Read More

crossdomain.xmlの有無を調べてたら出てびびった。クールすぎる。ここはセンスいいなあ。

muxtape404

Read More

別ドメインの画像を読み込む場合のLoaderクラスについていくつかメモ、クロスドメインポリシーファイル(crossdomain.xml)の読み込み方法と画像ロード速度について。結果的には画像20枚2M程度だと気にするほどでもなかった。Loaderクラスの詳細はAS で別ドメインの画像を読み込むときの注意点 – てっく煮ブログが参考になる。まずLoaderクラスの重要な性質を列挙。

  • crossdomain.xmlの設定に無関係に画像のロードは可能。(その後の画像の扱いにかかわらず必ずload()を行う)
  • 画像を画像として扱う(ただ画像を表示する)場合にはcrossdomain.xmlによる許可は必要ない。つまりどこの画像であろうと表示は可能。
  • 画像をデータとして扱う(サイズを変更するetc.)場合にはcrossdomain.xmlの許可が必要。
  • デフォルトでcrossdomain.xmlを読まない。(URLLoaderクラスは読む)

Loaderクラスを用いて別ドメインの画像をデータとして扱う方法は以下の二種類。どちらもロード完了後、(crossdomain.xmlで許可されている場合)画像データにアクセスが可能になる。

  1. Security.loadPolicyFile()関数でcrossdomain.xmlを読み込ませ、swfファイルに許可があることを知らせてからロードを始める。(実際にはcrossdomain.xmlを読み込むまでロードは自動待機)
  2. //Ex., media.tumblr.com
    Security.loadPolicyFile("http://media.tumblr.com/crossdomain.xml");
    var loader:Loader = new Loader();
    var req:URLRequest = new URLRequest("http://media.tumblr.com/xxx.jpg");
    loader.load(req);
    
  3. LoaderContextクラスのcheckPolicyFileプロパティを用いて、ロードごとにcrossdomain.xmlを読むよう指示する。
  4. var context:LoaderContext = new LoaderContext(true);
    var loader:Loader = new Loader();
    var req:URLRequest = new URLRequest("http://media.tumblr.com/xxx.jpg");
    loader.load(req, context);
    

上記二種の違いは、ロード前にcrossdomain.xmlを読み込ませるか、ロードごとにcrossdomain.xmlを読み込ませるかの違いでなので、画像のロード数が増えると両者の実行速度に差がでてくるのではないかと予想される(Security.loadPolicyFile()を使った方が早そう)。実際に調べてみた。以下はFirefoxのキャッシュを消去してからmedia.tumblr.comの画像20枚(1.7M)を読み込ませた結果をFirebugで表示したもの。左列に読み込んだファイル名一覧、右側にそれぞれ表示完了までにかかった時間が表示されている。

  1. Security.loadPolicyFile()関数を用いた場合 (11.75s)
  2. Security.loadPolicyFile()関数を用いた場合

  3. LoaderContextクラスを用いた場合 (13.21s)
  4. LoaderContextクラスを用いた場合

Security.loadPolicyFile()関数で一回だけcrossdomain.xmlを読み込んだ場合の方が完了が早くなっているが、実験のインターネット環境がよくないのであまり信用しないでください。場合によってはLoaderContextクラスでロードごとにcrossdomain.xmlを読み込ませる場合の方が早かったりもしたのでなんともいえない。結論としては両者に大差なし。というかLoaderContextクラスを用いた場合でも二回目以降同じcrossdomain.xmlの読み込みはキャッシュ化されてるから差がでてないのかもしれない。面白いのは上記二つの方法でcrossdomain.xmlをロードするタイミングが異なること。Security.loadPolicyFile()関数を用いた場合全画像をロード後、LoaderContextクラスを用いた場合画像ロード前にcrossdomain.xmlが読み込まれていて、イメージと逆なので不思議。日本の安定している快適なインターネット環境で誰か実験するのを期待しつつ、以下実験のソースコード。

package {
    import flash.display.Sprite;
    import flash.display.Loader;
    import flash.events.Event;
    import flash.net.URLRequest;
    import flash.system.LoaderContext;
    import flash.system.Security;
    
    public class LoadTest extends Sprite {
        public function LoadTest() {
            
            // 20 sexy images from media.tumblr.com
            var URLs:Array = [
            "http://media.tumblr.com/rJ6tpU7iW6y88so6TV84T1IG_500.jpg",
            "http://media.tumblr.com/z5Ki84RwK7e6bfe7zaGZCGUR_500.jpg",
            "http://media.tumblr.com/F2GLxhk3c6x444v1vma5BvIX_500.jpg",
            "http://media.tumblr.com/1gOzmif8i8oz2hz9GJ68aqBc_500.jpg",
            "http://media.tumblr.com/ky9cwXAKO8pyvjydMxesdNtr_500.jpg",
            "http://media.tumblr.com/7MmWU7n9s8l2gik4NKAlJjev_500.jpg",
            "http://media.tumblr.com/kcq2O9G2O8nggwncmiXj5Q8Z_500.jpg",
            "http://media.tumblr.com/5Vq1ESVSU8mn4n9raQLSFR6q_500.jpg",
            "http://media.tumblr.com/5Vq1ESVSU8mmwc4gekPbI9tl_500.jpg",
            "http://media.tumblr.com/0hxKQCklp2vdfmjiczTJn7v4_500.jpg",
            "http://media.tumblr.com/5Vq1ESVSU8mmtx38rZpmce95_500.jpg",
            "http://media.tumblr.com/0hxKQCklp2vdeqr003dmPyAo_500.jpg",
            "http://media.tumblr.com/0hxKQCklp4amrjwdD250Zy02_500.jpg",
            "http://media.tumblr.com/0hxKQCklp5kgx39bYjGWw6tB_500.jpg",
            "http://media.tumblr.com/0hxKQCklp6hoyaquSCmhzDXR_500.jpg",
            "http://media.tumblr.com/0hxKQCklp5xcqrjwzCIQggYR_500.jpg",
            "http://media.tumblr.com/0hxKQCklp6iz6is1QQJOQpi2_500.jpg",
            "http://media.tumblr.com/0hxKQCklp7swil3bMb5BHmae_500.jpg",
            "http://media.tumblr.com/FXGYbGlnr2pn7ftwSjgoCUtC_500.jpg",
            "http://media.tumblr.com/rJ6tpU7iW7smq0v5T9rpyo7w_500.jpg"];
            
            //loadModule1(URLs);
            loadModule2(URLs);
            
        }
        
        private function loadModule1(imgAry:Array):void{
            //Include crossdomain.xml from media.tumblr.com, once.
            Security.loadPolicyFile("http://media.tumblr.com/crossdomain.xml");
            
            var loaderAry:Array = new Array();
            
            for (var i:int=0; i<imgAry.length; i++){
                var loader:Loader = new Loader();
                var req:URLRequest = new URLRequest(imgAry[i]);
                loader.load(req);
                loader.contentLoaderInfo.addEventListener(Event.INIT,init);
                loaderAry.push(loader);
            }
        }
        
        private function loadModule2(imgAry:Array):void{      
            var loaderAry:Array = new Array();
            
            for (var i:int=0; i<imgAry.length; i++){
                var loader:Loader = new Loader();
                var req:URLRequest = new URLRequest(imgAry[i]);
                
                //Include crossdomain.xml per image (i.e., 20 times)
                var context:LoaderContext = new LoaderContext(true);
                loader.load(req,context);
                loader.contentLoaderInfo.addEventListener(Event.INIT,init);
                loaderAry.push(loader);
            }
        }
        
        private function init(e:Event):void{
            addChild(e.target.content);
        }
    }
}

Read More