Beautifl - Flash Gallery

Thumbnail : 万華鏡 - Kaleidoscope
万華鏡 - Kaleidoscope
enok 2009-09-03 MIT License

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

// 万華鏡 (KaleidoscopeController) by enok
//
// 画像はJPG, GIF, PNGに対応してます。サイズは200KBまでです。
// 画像サイズを400~600pxぐらいにするといい感じ。縦横の長さが違うと効果あります。
//
// http://linktale.net/

package {
    import flash.display.Sprite; 

    [SWF(backgroundColor="0x000000", frameRate="24")] 

    public class KaleidoscopeController extends Sprite { 
        public function KaleidoscopeController():void {
            //背景をしく
            graphics.beginFill(0x000000);
            graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
            graphics.endFill();
       
            //万華鏡を配置
            var trick:Kaleidoscope = new Kaleidoscope(this);
            addChild(trick);
        } 
    } 
}


import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BitmapDataChannel;
import flash.display.DisplayObjectContainer;
import flash.display.SimpleButton;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.utils.Timer;
//万華鏡本体
class Kaleidoscope extends Sprite {
    protected var _centerX:Number; //中心X
    protected var _centerY:Number; //中心Y
    protected var _imgBmp:Bitmap; //読込画像ビットマップ
    protected var _sec:Sector; //扇クラス
    protected var _partsBmpArray:Array; //万華鏡のパーツ配列
    protected var _partsAngle:Number = 30; //一つの万華鏡の角度
    protected var _ksRadius:Number = 0; //万華鏡の半径
    protected var _imgParam:Object = new Object(); //画像のパラメータ配列
    protected var _maskBmpData:BitmapData; //扇型マスクのビットマップデータ
    protected var _fileup:fileUpload; //ファイルアップクラス
    protected var _viewSp:Sprite; //万華鏡スプライト

    public function Kaleidoscope(s:DisplayObjectContainer):void {
        //中点
        _centerX = s.stage.stageWidth / 2;
        _centerY = s.stage.stageHeight / 2;

        //初期画像の取得
        var imgLoader:ImageLoader = new ImageLoader(this, "http://linkalink.jp/enok/wonderfl/kaleidoscope/images/sample/1.jpg");
        imgLoader.addEventListener(ImageLoader.LOAD_COMPLETE, init);
    }
   
    //ロード画像の初期化
    protected function init(e:Event):void {
        //画像変数初期化
        _imgParam["angle"] = 0.0;
        _imgParam["scale"] = 1.0;
        _imgParam["vx"] = 0.0;
        _imgParam["vec"] = 1;
       
        //画像のビットマップ化
        var loader:Loader = e.currentTarget.loader;

        var imgBmpData:BitmapData = new BitmapData(loader.width, loader.height);
        imgBmpData.draw(loader);
        _imgBmp = new Bitmap(imgBmpData);
       
        //長い方の片を万華鏡の半径にする
        _ksRadius = Math.min(Math.max(loader.width, loader.height), 420) / 2;
       
        //扇型クラス取得
        _sec = new Sector(0, 0, _partsAngle, 0, _ksRadius);
        //扇型マスクのBitmap化
        _maskBmpData = new BitmapData(_imgBmp.width, _imgBmp.height, true, 0x00000000);
        _maskBmpData.draw(_sec);
       
        //万華鏡パーツの設置
        _viewSp = new Sprite();
        _partsBmpArray = new Array()
        for (var i:uint = 0; i < Math.ceil(360 / _partsAngle); i++ ) {
            var partsBmp:Bitmap = new Bitmap();
            _partsBmpArray.push(partsBmp);
            _viewSp.addChild(partsBmp);
        }

        //追加
        addChild(_viewSp);
        addEventListener( Event.ENTER_FRAME, update );
       
        //ファイルアップ系
        _fileup = new fileUpload(this);
        _fileup.addEventListener(fileUpload.UP_COMPLETE, changeImgLoad);
    }
   
    //繰り返し処理
    protected function update(e:Event):void {
        copyKsParts( createKsParts() );
    }
   
    //扇型画像作成
    protected function createKsParts():BitmapData {       
        //画像ビットマップの変形処理
        var mat:Matrix = new Matrix();
        mat.translate( -_ksRadius, -_ksRadius);
        if (_ksRadius - stage.mouseX > 0) {
            _imgParam["vec"] = 1.0;
        }else {
            _imgParam["vec"] = -1.0;
        }
        var dist:Number = Math.sqrt(Math.pow(_centerX - stage.mouseX, 2) + Math.pow(_centerY - stage.mouseY, 2))
        _imgParam["vx"] = dist / _ksRadius * Math.PI * _imgParam["vec"];
        _imgParam["angle"] += _imgParam["vx"] * 0.02;
        mat.rotate(_imgParam["angle"]);
        _imgParam["scale"] = Math.min(1 / (dist / _ksRadius), 1.0);
        mat.scale(_imgParam["scale"], _imgParam["scale"]);

        //画像ビットマップマップデータ化
        var imgBmpData:BitmapData = new BitmapData(_imgBmp.height, _imgBmp.height, true, 0x00000000);
        imgBmpData.draw(_imgBmp, mat);
       
        //画像と扇型マスクを合体
        imgBmpData.copyChannel(_maskBmpData, new Rectangle(0, 0, _imgBmp.width, _imgBmp.height), new Point(0, 0), BitmapDataChannel.ALPHA, BitmapDataChannel.ALPHA);
       
        //処理後のビットマップマップデータを返す
        return imgBmpData;
    }
   
    //扇型画像をコピー
    protected function copyKsParts(imgBmpData:BitmapData):void {
        for (var i:uint = 0; i < _partsBmpArray.length; i++) {
            var mat:Matrix = new Matrix();
            //配置処理
            if (i % 2 != 0) {
                mat.scale( 1.0, -1.0);
                mat.rotate(_partsAngle * Math.PI / 180);
            }
            mat.rotate((i * _partsAngle - 90) * Math.PI / 180);
            mat.translate(_centerX, _centerY);
           
            //反映
            _partsBmpArray[i].transform.matrix = mat;
            _partsBmpArray[i].bitmapData = imgBmpData;
        }
    }
   
    //画像変更
    protected function changeImgLoad(e:Event):void {
        //画像ロード
        var imgLoader:ImageLoader = new ImageLoader(this, "http://linkalink.jp/enok/wonderfl/kaleidoscope/images/users/"+_fileup.name);
        imgLoader.addEventListener(ImageLoader.LOAD_COMPLETE, changeImg);
    }
   
    //画像変更処理
    protected function changeImg(e:Event):void {
        //画像のビットマップ化
        var loader:Loader = e.currentTarget.loader;
        var imgBmpData:BitmapData = new BitmapData(loader.width, loader.height);
        imgBmpData.draw(loader);
        _imgBmp = new Bitmap(imgBmpData);
       
        //万華鏡半径算出
        _ksRadius = Math.min(Math.max(loader.width, loader.height), 420) / 2;
    }
}

//扇型クラス
//sketchbook参考にさせて頂きました
class Sector extends Sprite {
    public function Sector(px:Number, py:Number, partsRotate:Number, startAngle:Number, ksRadius:Number):void {
        var points:Array = getArcPoints(px, py, ksRadius, partsRotate, startAngle, 180);
        points.unshift( new Point(x, y) );
        drawLines(points);
    }
   
    //扇形、円弧と中心点を結んだ形状を算出
    private function getArcPoints(x:Number, y:Number, radius:Number, degree:Number, fromDegree:Number=0, split:Number=36):Array{
        var points:Array = new Array();
        var fromRad:Number = fromDegree * Math.PI / 180;
        var dr:Number = (degree * Math.PI / 180) / split;
       
        for(var i:int=0; i<split + 1; i++){
            var pt:Point = new Point();
            var rad:Number = fromRad + dr * i;
            pt.x = Math.cos(rad) * radius + x;
            pt.y = Math.sin(rad) * radius + y;
            points.push(pt);
        }
       
        return points;
    }
   
    //扇を描画
    public function drawLines(points:Array):void {
        graphics.lineStyle(1, 0xffffff);
        graphics.beginFill(0xffffff);
        graphics.moveTo(points[0].x, points[0].y);
        for(var i:Number=1; i<points.length; i++){
            graphics.lineTo(points[i].x, points[i].y);
        }
    }
}

import flash.events.ProgressEvent;
import flash.events.IOErrorEvent;
import flash.events.TimerEvent;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.system.LoaderContext;
import flash.net.URLRequest;
//画像ローダーディスパッチャー
class ImageLoader extends EventDispatcher {
    public static const LOAD_COMPLETE:String = "LOAD_COMPLETE";
    public static const IOERROR:String = "IOERROR";
    protected var _loader:Loader;
    protected var _sv:statusView;
    protected var _timer:Timer;
   
    public function ImageLoader(s:DisplayObjectContainer , url:String) {
        //登録
        _loader = new Loader();
        _loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgress);
        _loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaded);
        _loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, error);

        //セキュリティ
        var loaderContext:LoaderContext = new LoaderContext(true);

        //画像ロード開始
        var urlReq:URLRequest = new URLRequest(url);
        _loader.load(urlReq, loaderContext);

        //ステータス表示
        _sv = new statusView();
        _sv.view("IMAGE LOAD START");
        s.addChild(_sv);
        s.setChildIndex(_sv, 0);
    }
   
    //画像ロード完了
    protected function onLoaded(e:Event):void {
        _sv.view("");
        dispatchEvent(new Event(LOAD_COMPLETE));
    }

    //画像ロード中
    protected function onProgress(e:ProgressEvent):void {
        _sv.view("IMAGE LOADING...");
        //var per:Number = Math.round(e.bytesLoaded / e.bytesTotal * 100);
    }
   
    //エラー
    protected function error(e:IOErrorEvent):void {
        _sv.view("IMAGE LOAD ERROR");
        _timer = new Timer(3000, 1);
        _timer.addEventListener(TimerEvent.TIMER, onTimer);
        _timer.start();

        dispatchEvent(new Event(IOERROR));
    }
    
    protected function onTimer(event:TimerEvent):void {
        _sv.view("");
        _timer.removeEventListener(TimerEvent.TIMER, onTimer);
    }
   
    //プロパティ
    public function get loader():Loader { return _loader;}
}

import flash.events.EventDispatcher;
import flash.events.MouseEvent;
import flash.net.FileReference;
import flash.net.FileFilter;
//ファイルアップディスパッチャ
class fileUpload extends EventDispatcher {
    public static const UP_COMPLETE:String = "UP_COMPLETE";
    protected var s:DisplayObjectContainer;
    protected var fileRef:FileReference;;
    protected var uploadURL:URLRequest;
    protected var _sv:statusView;
   
    public function fileUpload(ps:DisplayObjectContainer):void {
        s = ps;
       
        //初期化
        fileRef = new FileReference();
        fileRef.addEventListener(Event.SELECT, onSelect);
        fileRef.addEventListener(ProgressEvent.PROGRESS , onProgress); 
        fileRef.addEventListener(Event.COMPLETE, onComplete);
        uploadURL = new URLRequest();
        uploadURL.url = "http://linkalink.jp/enok/wonderfl/kaleidoscope/upload.php";
       
        //ボタン設置
        var upBtn:customButton = new customButton(s);
        upBtn.x = 5;
        upBtn.y = s.stage.stageHeight - upBtn.height - 5;
       
        //ステータス表示
        _sv = new statusView();;
        s.addChild(_sv);
        s.setChildIndex(_sv, 0);
        
        //追加
        s.addChild(upBtn);
        upBtn.addEventListener(MouseEvent.MOUSE_DOWN, function(event:Event):void{ var fileFilter:FileFilter = new FileFilter("Images", "*.jpg;*.gif;*.png"); fileRef.browse([fileFilter]);});
    }
   
    //ファイル選んだ時
    protected function onSelect(e:Event):void {
        var fileRef:FileReference = FileReference(e.target);
        fileRef.upload(uploadURL);
    }
    
    //アップが完了
    protected function onProgress(e:Event):void {
        _sv.view("FILE UPLOADING...");
    }
   
    //アップが完了
    protected function onComplete(e:Event):void {
        _sv.view("");
        dispatchEvent(new Event(UP_COMPLETE));
    }
   
    //プロパティ
    public function get name():String {
        return fileRef.name;
    }
}

import flash.geom.ColorTransform;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.TextFieldAutoSize;
//ボタンクラス
class customButton extends SimpleButton {
    private var s:DisplayObjectContainer;
   
    public function customButton(ps:DisplayObjectContainer):void {
        s = ps;
       
        //ボタンの色とか設定
        var state:Sprite = makeUpstate();
        state.transform.colorTransform = new ColorTransform(1, 1, 1, 0.9, 0, 0, 0, 0);
        downState = upState = state;
        state = makeUpstate();
        state.transform.colorTransform = new ColorTransform(1, 1, 1, 0.8, 0, 0, 0, 0);
        overState = state;
        useHandCursor = true;
        hitTestState = upState;
    }
   
    //ボタン画像作成
    protected function makeUpstate():Sprite {
        var state:Sprite = new Sprite();
        var w:Number = 50;
        var h:Number = 10;
        var margin:Number = 2;
       
        //枠系
        state.graphics.lineStyle(1, 0xffffff);
        state.graphics.drawRect(0, 0, w + margin * 2, h + margin * 2);
        state.graphics.endFill();
        state.graphics.beginFill(0xffffff);
        state.graphics.drawRect(margin, margin, w, h);
        state.graphics.endFill();
       
        //テキスト系
        var txtFormat:TextFormat = new TextFormat();
        txtFormat.font = "_sans";
        txtFormat.size = 10;
        txtFormat.color = 0xcc2222;
        txtFormat.bold = true;
        var txtField:TextField = new TextField();
        txtField.defaultTextFormat = txtFormat;
        txtField.text = "CHANGE";
        txtField.autoSize = TextFieldAutoSize.LEFT;
        txtField.x = 4;
        txtField.y = -1;
        state.addChild(txtField);
       
        return state;
    }
}

class statusView extends Sprite {
    protected var _txtField:TextField;
        
    public function statusView():void {
        //テキスト系
        var txtFormat:TextFormat = new TextFormat();
        txtFormat.font = "_sans";
        txtFormat.size = 10;
        txtFormat.color = 0xeeeeee;
        //txtFormat.bold = true;
        _txtField = new TextField();
        _txtField.defaultTextFormat = txtFormat;
        _txtField.text = "";
        _txtField.autoSize = TextFieldAutoSize.LEFT;
        _txtField.x = 0;
        _txtField.y = 0;
        addChild(_txtField);
    }
    
    public function view(mess:String):void {
        _txtField.text = mess;
    }
    
}