Beautifl - Flash Gallery

Thumbnail : Broken Pieces
Broken Pieces
uwi 2010-08-31 MIT License

再生するにはFlash Playerが必要です。デスクトップのブラウザでご覧ください。

// お好きなBGMを流しながらどうぞ。
// マウスオーバーでサムネイル
// クリックで画像ページに飛ぶよ
// 
// @see http://www.nicovideo.jp/watch/sm11464969
// @see http://www.youtube.com/watch?v=x08R89_zRSs
package {
    import flash.display.*;
    import flash.events.*;
    import flash.net.*;
    import flash.geom.*;
    import flash.ui.*;
    import flash.system.LoaderContext;
    import flash.text.*;
    import flash.filters.*;
    import flash.utils.*;
    import org.libspark.betweenas3.*;
    import org.libspark.betweenas3.easing.*;
    import org.libspark.betweenas3.tweens.*;
    import com.bit101.components.*;
    
    [SWF(backgroundColor="#f3f3f3")]
    public class BrokenPieces extends Sprite {
        private const FEEDURL : String = "http://api.flickr.com/services/feeds/photos_public.gne?format=rss_200&tags=";
        private const media : Namespace = new Namespace("http://search.yahoo.com/mrss/");
        
        // stageの大きさ情報
        private var SW : Number;
        private var SH : Number;
        private const N : uint = 7; // 断片の個数
        
        private var _msg : Label; // メッセージボックス
        private var _searchBox : Text; // タグクエリ入力
        private var _searchBtn : PushButton; // タグ検索ボタン
        private var _thumb : Bitmap; // サムネイル
        
        private var _ul : URLLoader; // フィードローダー
        private var _loadingQueue : Array; // 未ロードのpicture <String>
        private var _loadingTween : ITween; // ローディング用tween
        private var _urlInfoMap : Object = {}; // ロード中の、URLとpictureの情報を対応

        private var _nValid : uint; // フィード内にある有効なpictureの個数
        private var _blurs : Array; // ピンぼけ用blur <Array<BlurFilter>>
        private var _parts : Array; // 断片のリスト <ExSprite>
        private var _step : Number = -9999999; // 時間
        private var _useBlur : Boolean; // ブラー使用フラグ
        private var _selectSmall : Boolean; // 小サイズ限定フラグ
        
        private var _tf : TextField; // デバッグ用
        private var _jobs : Array; // 負荷分散用ジョブ配列
        
        public function BrokenPieces() {
            Wonderfl.capture_delay(20);
            
            _tf = new TextField();
            _tf.width = 465;
            _tf.height = 465;
            _tf.y = 250;
//            addChild(_tf);
            
//            stage.scaleMode = "noScale";
            SW = stage.stageWidth;
            SH = stage.stageHeight;
            
            _parts = [];
            _loadingQueue = [];
            _jobs = [];
            
            // ブラー格納
            _blurs = [[]];
            for(var i : uint = 1;i < 100;i++){
                _blurs.push([new BlurFilter(i, i)]);
            }
            
            _ul = null;
            addEventListener(Event.ENTER_FRAME, onEnterFrame);
            
            _searchBox = new Text(this, 10, SH - 23);
            _searchBox.height = 21;
            _searchBox.editable = true;
            // 日本語対応
            _searchBox.textField.embedFonts = false;
            _searchBox.textField.defaultTextFormat = new TextFormat(null, 9, Style.INPUT_TEXT);
            
            // エンター決定対応
            var flagEnter : Boolean = false;
            _searchBox.textField.addEventListener(KeyboardEvent.KEY_DOWN, function(e:KeyboardEvent) : void {if(e.keyCode == Keyboard.ENTER){flagEnter = true; }});
            _searchBox.textField.addEventListener(TextEvent.TEXT_INPUT, function(e:TextEvent) : void {if(flagEnter){e.preventDefault();}});
            _searchBox.textField.addEventListener(KeyboardEvent.KEY_UP, function(e:KeyboardEvent) : void {if(e.keyCode == Keyboard.ENTER && flagEnter){onTagSearch(null);} flagEnter = false; });
            _searchBtn = new PushButton(this, 220, SH - 23, "TagSearch!", onTagSearch);
            _searchBtn.setSize(70, 22);
            
            var checkBlur : CheckBox = new CheckBox(this, 300, SH - 30, "Blur", function(e:MouseEvent) : void {_useBlur = e.currentTarget.selected; });
            checkBlur.selected = true;
            _useBlur = true;
            
            var checkSmall : CheckBox = new CheckBox(this, 300, SH - 15, "Small pictures only", function(e:MouseEvent) : void {_selectSmall = e.currentTarget.selected; });
            checkSmall.selected = true;
            _selectSmall = true;
            
            _thumb = new Bitmap();
            _thumb.x = 400;
//            _thumb.y = SH - 90;
            addChild(_thumb);
            
            _msg = new Label(this, 10, SH - 43);
            _msg.autoSize = true;
            _loadingTween = null;
            
            _nValid = 999;
        }
        
        // タグ検索
        private function onTagSearch(e : MouseEvent) : void
        {
            if(_ul != null){
                _ul.removeEventListener(Event.COMPLETE, onFeedLoadComplete);
                _ul.close();
            }
            _ul = new URLLoader();
            _ul.addEventListener(Event.COMPLETE, onFeedLoadComplete);
            _ul.load(new URLRequest(FEEDURL + encodeURIComponent(_searchBox.text)));
            _msg.text = "Loading feed..";
            _loadingQueue = [];
            if(_loadingTween != null){
                _loadingTween.stop();
                _loadingTween = BetweenAS3.to(_msg, {x:10}, 1.0);
                _loadingTween.play();
            }
        }
        
        private function onEnterFrame(e : Event) : void
        {
            if(_loadingQueue.length == 0){
                // ロード中のフィードがなく、フィード内の有効なpictureが存在するとき、フィードをロード
                if(_ul == null && _nValid > 0){
                    _ul = new URLLoader();
                    _ul.addEventListener(Event.COMPLETE, onFeedLoadComplete);
                    _ul.load(new URLRequest(FEEDURL + encodeURIComponent(_searchBox.text)));
                    _msg.text = "Loading feed..";
                }
            }else{
                // 6秒ごとに、断片の数が20個以下のときloadingQueueにあるpictureをひとつロード
                if(_step % 180 == 0 && _parts.length <= 20){
                    var l : Loader = new Loader();
                    l.load(new URLRequest(_loadingQueue.pop()), new LoaderContext(true));
                    l.contentLoaderInfo.addEventListener(Event.COMPLETE, onPictureLoadComplete);
                }
            }

            stepJobs();
            stepParts();
            _step++;
        }
        
        // 断片を動かす
        private function stepParts() : void
        {
            for(var i : int = 0;i < _parts.length;i++){
                var sp : ExSprite = _parts[i];
                if(sp.y >= SH){
                    // 断片消去処理
                    removeChild(sp);
                    sp.removeEventListener(MouseEvent.CLICK, onPartClick);
                    sp.removeEventListener(MouseEvent.MOUSE_OVER, onPartOver);
                    sp.removeEventListener(MouseEvent.MOUSE_OUT, onPartOut); 
                    sp.bmp.bitmapData.dispose();
                    sp.thumb.dispose();
                    if(i == _parts.length - 1){
                        _parts.pop();
                    }else{
                        _parts[i] = _parts.pop();
                        i--;
                    }
                    continue;
                }
                sp.x += sp.v[0];
                sp.y += sp.v[1];
                if(_useBlur){
                    var oaz : int = Math.abs(sp.z) / 10;
                    sp.z += sp.v[2];
                    var az : int = Math.abs(sp.z) / 10;
                    if(oaz != az || sp.alpha == 0){
                        sp.bmp.filters = _blurs[az]; // 必要なときだけフィルタを変更
                    }
                }else{
                    sp.z += sp.v[2];
                    sp.bmp.filters = null;
                } 

                sp.rotationX += sp.omega[0];
                sp.rotationY += sp.omega[1];
                sp.rotationZ += sp.omega[2];
                if(sp.alpha < 1)sp.alpha += 0.05;
            }
        } 
        
        // pictureを断片に分割
        // pictureに断片の種をばらまいて成長させていく。
        // 色差が激しい時は乗り越えるのに時間がかかるようにする。
        public function stepJobs() : void
        {
            if(_jobs.length == 0)return;
            var n : uint = 10000 / _jobs.length;
            for(var i : int = 0;i < _jobs.length;i++){
                var job : Object = _jobs[i];
                var p : uint = job.p;
                var map : BitmapData = job.map;
                var q : Vector.<uint> = job.q;
                var base : BitmapData = job.base;
                var xsums : Vector.<Number> = job.xsums;
                var ysums : Vector.<Number> = job.ysums;
                var nums : Vector.<uint> = job.nums;
                
                var step : uint = 0;
                for(;p < q.length && step <= n;step++){
                    var x : uint = q[p++];
                    var y : uint = q[p++];
                    var left : uint = q[p++];
                    if(left >= 1){
                        q.push(x, y, left - 1);
                    }else{
                        var c : uint = map.getPixel(x, y);
                        xsums[c-1] += x;
                        ysums[c-1] += y;
                        nums[c-1]++;
                        var oc : uint = base.getPixel(x, y);
                        for(var u : int = -1;u <= 1;u++){ 
                            for(var v : int = -1;v <= 1;v++){
                                if(u == 0 && v == 0)continue;
                                var nx : int = x + u;
                                var ny : int = y + v; 
                                if(nx >= 0 && nx < map.width && ny >= 0 && ny < map.height && map.getPixel(nx, ny) == 0){
                                    var dc : Number = deltaColor(base.getPixel(nx, ny), oc);
                                    map.setPixel(nx, ny, c);
                                    q.push(nx, ny, Math.min(dc / 7, 20));
                                }
                            }
                        }
                    }
                }
                
                if(p >= q.length){
                    makeParts(map, base, job.thumb, job.info, job.xsums, job.ysums, job.nums);
                    if(i == _jobs.length - 1){
                        _jobs.pop();
                    }else{
                        _jobs[i] = _jobs.pop();
                        i--;
                    }
                }else{
                    job.p = p;
                }
            }
        }
       
        // フィード読み込み完了時
        public function onFeedLoadComplete(e : Event) : void
        {
            _ul.removeEventListener(Event.COMPLETE, onFeedLoadComplete);
            var xl : XMLList = XML(_ul.data)..media::content;
            var len : uint = xl.length();
            _nValid = 0;
            for(var i : uint = 0;i < len;i++){
                if(!_selectSmall || xl[i].@height * xl[i].@width <= 1500 * 1500){
                    _nValid++;
                    _urlInfoMap[xl[i].@url.toXMLString()] = {
                        link : XML(_ul.data)..link[i+2].text().toXMLString(),
                        thumbnail : XML(_ul.data)..media::thumbnail[i].@url.toXMLString()
                    }
//                    tr(XML(_ul.data)..link[i+1].text().toXMLString());
//                    tr(XML(_ul.data)..media::thumbnail[i].@url.toXMLString());
  
                    _loadingQueue.push(xl[i].@url.toXMLString());
                }
            }
            _msg.text = len == 0 ? "Contents not found." : (_nValid == 0 ? "Every content is too large!" : "Loading contents...");
            if(_nValid > 0){
                if(_loadingTween != null)_loadingTween.stop();
                 _loadingTween = BetweenAS3.repeat(
                    BetweenAS3.serial(
                        BetweenAS3.to(_msg, {x:200}, 0.5, Cubic.easeInOut),
                        BetweenAS3.to(_msg, {x:10}, 0.5, Cubic.easeInOut)
                        ), 9999);
                 _loadingTween.play();
            }
            _ul = null;
        }
        
        // picture読み込み完了時
        public function onPictureLoadComplete(e : Event) : void
        {
            e.currentTarget.removeEventListener(Event.COMPLETE, onPictureLoadComplete);
            _msg.text = "Enjoy!";
            if(_loadingTween != null){
                _loadingTween.stop();
                _loadingTween = BetweenAS3.to(_msg, {x:10}, 1.0);
                _loadingTween.play();
            }
            
            var l : Loader = e.currentTarget.loader as Loader;
            
            // 縮小
            var shrink : Number = 200 / Math.sqrt(l.width * l.height);
            if(shrink > 1.0)shrink = 1.0;
            var base : BitmapData = new BitmapData(l.width * shrink, l.height * shrink, false, 0x000000);
            base.lock();
            base.draw(l.content, new Matrix(shrink, 0, 0, shrink), null, null, null, true);
//            tr(l.width, l.height, shrink);

            // サムネイル用に縮小
            var shrink2 : Number = 80 / Math.sqrt(l.width * l.height);
            if(shrink2 > 1.0)shrink2 = 1.0;
            var thumb : BitmapData = new BitmapData(l.width * shrink2, l.height * shrink2, false, 0x000000);
            thumb.lock();
            thumb.draw(l.content, new Matrix(shrink2, 0, 0, shrink2), null, null, null, true);
             
            var i : uint;
            // 断片の重心計算用
            var xsums : Vector.<Number> = new Vector.<Number>(N);
            var ysums : Vector.<Number> = new Vector.<Number>(N);
            var nums : Vector.<uint> = new Vector.<uint>(N);
            for(i = 0;i < N;i++){
                xsums[i] = 0;
                ysums[i] = 0;
                nums[i] = 0;
            }

            // 断片情報格納用
            var map : BitmapData = new BitmapData(base.width, base.height, false, 0);
            map.lock();
            
            var q : Vector.<uint> = new Vector.<uint>();
            for(i = 0;i < N;i++){
                var x : uint = Math.random() * base.width;
                var y : uint = Math.random() * base.height;
                map.setPixel(x, y, i + 1);
                q.push(x, y, 0);
            }
            
            _jobs.push({
                map : map,
                p : 0,
                q : q,
                base : base,
                thumb : thumb,
                info : _urlInfoMap[l.contentLoaderInfo.url],
                xsums : xsums,
                ysums : ysums,
                nums : nums
            });
            _urlInfoMap[l.contentLoaderInfo.url] = null;
            l.unload();
        }

        private function makeParts(map : BitmapData, base : BitmapData, thumb : BitmapData, info : Object, xsums : Vector.<Number>, ysums : Vector.<Number>, nums : Vector.<uint>) : void
        {
            // 断片実体の作成
            var targX : Number = SW / 2 + Math.random() * SW / 2.5 - SW / 5; // 合体予定座標
            var targY : Number = SH / 2 + Math.random() * SH / 2.5 - SH / 5;
            var T : Number = targY + SW / 3; // 合体予定時刻
            var i : uint;
            for(i = 0;i < N;i++){
                var bb : BitmapData = new BitmapData(base.width, base.height, true, 0);
                bb.copyPixels(base, base.rect, new Point());
                bb.threshold(map, map.rect, new Point(), "!=", i+1, 0, 0xffffff);
                
                var sp : ExSprite = new ExSprite();
                var g : Array = [xsums[i] / nums[i], ysums[i] / nums[i]];
                var bmp : Bitmap = new Bitmap(bb);
                bmp.x = -g[0];
                bmp.y = -g[1];
                sp.addChild(bmp);
                _parts.push(sp);
                addChildAt(sp, 0);
//                sp.mouseEnabled = false;
                sp.mouseChildren = false;
                
                sp.v = [
                    (Math.random() - 0.5) * 1.5,
                    (Math.random() + 0.5) * 1,
                    (Math.random() + 0.5) * -1
                    ]; // 並進速度
                sp.omega = [
                    (Math.random() - 0.5) * 2,
                    (Math.random() - 0.5) * 2,
                    (Math.random() - 0.5) * 0
                    ]; // 回転速度
                sp.bmp = bmp;
                sp.alpha = 0.0;
                sp.x = targX - base.width / 2 + g[0] - sp.v[0] * T;
                sp.y = targY - base.height / 2 + g[1] - sp.v[1] * T;
                sp.z = 0 - sp.v[2] * T;
                sp.rotationX = -sp.omega[0] * T;
                sp.rotationY = -sp.omega[1] * T;
                sp.rotationZ = -sp.omega[2] * T;
                sp.thumb = thumb.clone();

                sp.info = info;
                sp.addEventListener(MouseEvent.CLICK, onPartClick);
                sp.addEventListener(MouseEvent.MOUSE_OVER, onPartOver);
                sp.addEventListener(MouseEvent.MOUSE_OUT, onPartOut); 
            }
            thumb.dispose();
            base.dispose();
            var gg : Number = getTimer();
        }
        
        // 断片をクリックしたら画像を開く
        private function onPartClick(e : MouseEvent) : void
        {
             navigateToURL(new URLRequest(e.currentTarget.info.link), "_blank");
        }
        
        // 断片にマウスオーバーしたらサムネイルを表示
        private function onPartOver(e : MouseEvent) : void
        {
            _thumb.bitmapData = e.currentTarget.thumb;
            _thumb.y = SH - 5 - _thumb.bitmapData.height;
        }
        
        // 断片からマウスアウトしたらサムネイルを消す
        private function onPartOut(e : MouseEvent) : void
        {
            _thumb.bitmapData = null;
        }
        
        // 色差を計算
        public function deltaColor(a : uint, b : uint) : Number
        {
            var ra : uint = (a >> 16) & 0xff;
            var ga : uint = (a >> 8) & 0xff;
            var ba : uint = (a >> 0) & 0xff;
            var rb : uint = (b >> 16) & 0xff;
            var gb : uint = (b >> 8) & 0xff;
            var bb : uint = (b >> 0) & 0xff;
            return Math.abs(ra - rb) + Math.abs(ga - gb) + Math.abs(ba - bb);
        }
        
        private function tr(...o : Array) : void
        {
            _tf.appendText(o + "\n");
            _tf.scrollV = _tf.maxScrollV;
        }
    }
}

import flash.display.*;

class ExSprite extends Sprite
{
    public var v : Array;
    public var omega : Array;
    public var bmp : Bitmap;
    public var thumb : BitmapData;
    public var info : Object;
}