processing.jsで楽しんだ

機能的にはこれの模倣/コピペ

ref : http://www.few.vu.nl/~eliens/media/@js-sample-canvas-particles.txt

こうなった

make : http://modeverv.dyndns.org/sample/processing/
made : http://modeverv.dyndns.org/sample/processing/processing.html
パーティクルをflashで作った時よりも速いプログラムな気がしている。
jsでこういう遊びができるのは非常に便利。macユーザーなのでwindowsをわざわざ持ち出し、flashdevelopを立ち上げ、プロジェクトをつくってetcというのはしんどい。

ソース

var debug = function(s){
    //          console.log(s)  ;
};
// できるだけ重複処理を省かないとCPU/メモリ食いまくり
// if文を並べるよりif-else if-elseでいくとテストの回数が減る
// for文の初期化を太らせてイテレーションで計算させないとか
// 見通しのよさとかオブジェクトに閉じ込めてメッセージを送りましょうとか
// かっこいいけど遅いので臨機応変に
function do_processing(){
    var sketchProc = function(processing) {
        // config
        // バックグラウンドの色
        var BG = { r:0,g:0,b:0};
        var DrawBG = false;
        // パーティクルの数
        var PARTICLE_NUM = 100;
        // FPS
        var FPS = 30;
        var _canvas = document.getElementById("sketch");
        var W = _canvas.width;
        var H = _canvas.height;

        // パーティクルのプール
        var PARTICLE_POOL = [];
        // トランジションの定義 TRANSITIONELEMENTをくみあわせる
       //  毎度 無名functionの生成が走るので遅いのかも。ブラウザが賢くやってくれるのかも。
        var TRANSITIONS = [
            function gravity(){
                TRANSITION_HANDLER(this,
                                   function(pixel){
                                       pixel = _TRANSITON_ELEMENTS.bounce(pixel);
                                       pixel = _TRANSITON_ELEMENTS.gravity(pixel);
                                   }
                                  );
            },
            function beyond_gravity(){
                TRANSITION_HANDLER(this,
                                   function(pixel){
                                       pixel = _TRANSITON_ELEMENTS.bounce(pixel);
                                       pixel = _TRANSITON_ELEMENTS.beyond_gravity(pixel);
                                   }                                   
                                  );
            },
            function gravityB(){
                TRANSITION_HANDLER(this,
                                   function(pixel){
                                       pixel = _TRANSITON_ELEMENTS.hantai(pixel);
                                       pixel = _TRANSITON_ELEMENTS.gravity(pixel);
                                   }
                                  );
            },
            function beyond_gravityB(){
                TRANSITION_HANDLER(this,
                                   function(pixel){
                                       pixel = _TRANSITON_ELEMENTS.hantai(pixel);
                                       pixel = _TRANSITON_ELEMENTS.beyond_gravity(pixel);
                                   }                                   
                                  );
            }
            /*, きもい
             function impulsWH(){
             TRANSITION_HANDLER(this,_TRANSITON_ELEMENTS.impulsWH);
             }*//*,
                 てんかんになりかける
                 function impulsColor(){
                 TRANSITION_HANDLER(this,_TRANSITON_ELEMENTS.impulsColor);
                 },
                 function Flash(){
                 TRANSITION_HANDLER(this,_TRANSITON_ELEMENTS.flash);
                 }
                 */
        ];
        // 各トランジションの動き
        var _TRANSITON_ELEMENTS = {
            gravity:function(pixel){
                pixel.vy = (pixel.vy + 0.1);
                return pixel;
            },
            beyond_gravity:function(pixel){
                pixel.vy = (pixel.vy - 0.1);
                return pixel;
            },
            impulsWH : function(pixel){
                pixel.w = pixel.h = 30 + 10 * Math.cos(pixel.counter);
                return pixel;
            },
            flash : function(pixel){
                pixel.cR = pixel.cG = pixel.cB = (255 + 255 * Math.cos(pixel.counter)) % 255 ;
                return pixel;
            },
            impulsColor :function(pixel){
                var C = (255 + 255 * Math.cos(pixel.counter)) % 255 ;
                if(pixel.cR < 240){
                    pixel.cR = C;
                }else if(pixel.cG < 240){
                    pixel.cG += C;
                }else if(pixel.cB < 240){
                    pixel.cB += C;
                }else{
                    pixel.cR = pixel.cG = pixel.cB = 0;
                }
                return pixel;
            },
            bounce :function(pixel){
                if(pixel.x < 0){
                    pixel.x = 0;
                    pixel.vx*=-1;
                }
                if(pixel.x + pixel.w  > W){
                    pixel.x = W - pixel.w;
                    pixel.vx*=-1;
                }
                if(pixel.y < 0){
                    pixel.y = 0;
                    pixel.vy*=-1;
                }
                if(pixel.y + pixel.h  > H){
                    pixel.y = H - pixel.h;
                    pixel.vy*=-1;
                }
                return pixel;
            },
            hantai : function(pixel){
                // 反対側
                if(pixel.x < 0){
                    pixel.x = W;
                }else if(pixel.x + pixel.w > W){
                    pixel.x = 0;
                }

                if(pixel.y < 0){
                    pixel.y = H;
                }else if(pixel.y + pixel.h > H){
                    pixel.y = 0;
                }
                return pixel;
            }
        };
        // カウンターが+の場合はトランジションをキックする。-の場合はPixelの中身を洗い替え
        var TRANSITION_HANDLER = function(pixel,proc){
            if(pixel.counter > 0) {
                pixel.counter -= 1;
                proc(pixel);
            }else{
                pixel.init();
            }
        };
        
        var DESTINATIONFLG = {
            initial : -1,
            moving  : 0,
            moved   : 1,
            finish  : FPS * 3,
            gaman   : FPS
        };
        // 指定場所に動かすトランジション 現状中身は一つだけ ちょっとださい
        var TRANSITIONS_DESTINATION = [
            function(){
                // counterの数でdst_x,dst_yにたどり着く
                this.counter -= 1;
                if(this.setflg == DESTINATIONFLG.initial ){
                    this.vx = (this.dst_x - this.x) / this.counter;
                    this.vy = (this.dst_y - this.y) / this.counter;
                    this.va = 120 / this.counter;
                    this.cA = 10;
                    this.setflg = 0;
                }else if(this.setflg == DESTINATIONFLG.moving){
                    if(this.counter < 0){
                        this.vx = this.vy = 0;
                        this.setflg = DESTINATIONFLG.moved;
                    }
                }else if(this.setflg >= DESTINATIONFLG.moved && this.setflg <= DESTINATIONFLG.gaman){
                    this.setflg += 1;
                    this.cA = 255;
                    _TRANSITON_ELEMENTS.flash(this);
                }else if(this.setflg > DESTINATIONFLG.gaman ){
                    DrawBG = false; // backgroundが動くように戻す
                    this.init();
                }else{
                    // safety
                    this.init();
                }
                this.cA += this.va;
            }
        ];
        // 指定場所の定義
        var DESTINATION = [
            // キャンバス中央
            [[Math.floor(W/2),Math.floor(H/2)]],
            // 左上隅
            [[100,100]],
            // 右上隅
            [[W-100,100]],
            // 左下隅
            [[100,H-100]],
            // 左下隅
            [[W-100,H-100]],
            // 100格子
            (function kousi(interval){
                 var ret = [];
                 for(var i=0,l=Math.ceil(W/interval);i<l;i++){
                     for(var j=0,n=Math.ceil(H/interval);j<n;j++){
                         ret.push([i*interval,j*interval]);
                     }
                 }
                 return ret;
             })(100),
            // 十字架
            (function cross(interval){
                 var ret = [];
                 var cW = W/2;
                 var cH = H/2;
                 for(var i=0,l=Math.ceil(W/interval);i<l;i++){
                     ret.push([i * interval,cH]);
                 }
                 for(var i=0,l=Math.ceil(H/interval);i<l;i++){
                     ret.push([cW,i* interval]);
                 }
                 return ret;
             })(100)
        ];
        
        // パーティクルオブジェクト
        var Pixel = function(){ 
            this.x = Math.floor(Math.random() * W);
            this.y = Math.floor(Math.random() * H);
            this.init();
        };
        // コメント:トランジションの関数を内蔵するとカッコいいかもしれませんが。。。
        // 各Pixelの内部が巨大になり遅くなる、かつPixel間でトランジション関数は重複する無意味さなので
        // トランジション関数は外に出します。
        Pixel.prototype = {
            // 初期化関数
            // x,y座標,加速度vx,vy,色cR,cG,cB,cA,トランジション,カウンターを適当にセット
            init : function(){
                this.w = Math.floor(Math.random() * 10) + 10;
                this.h = this.w;
                var seed = Math.random();
                this.vx = Math.floor(Math.random() * 10);
                this.vy = Math.floor(Math.random() * 10);
                if(seed > 0.5){
                    this.vx *= -1;
                    this.vy *= -1;
                }
                this.cR = Math.floor(Math.random()* 255);
                this.cG = Math.floor(Math.random()* 255);
                this.cB = Math.floor(Math.random()* 255);
                this.cA = Math.floor(Math.random()* 255);
                this.transition = TRANSITIONS[Math.floor(Math.random() * TRANSITIONS.length)];
                this.counter =  3 * (FPS + FPS * Math.random()) ;
            },
            // 描画関数 トランジションを行ったあと描画する
            draw:function(){
                this.transition();
                this.x += this.vx;
                this.y += this.vy;
                processing.fill(this.cR,this.cG,this.cB,this.cA);
                processing.ellipse(
                    Math.floor(this.x % W), 
                    Math.floor(this.y % H),
                    this.w,
                    this.h
                );
            }
        };

        //  パーティクルプールにPixelをnewしてpush
        for(var i=0;i<PARTICLE_NUM;i++){
            PARTICLE_POOL.push(new Pixel());
        }
        // Processingのサイズやfpsを色々
        processing.setup = function(){
            processing.size(W, H);
            processing.noStroke();
            processing.frameRate( FPS );
        };
        // トランジションが指定場所モードに変わったときに
        // canvasを塗り直さないようにするフラグ 
        // モードの変更にnew Dateを利用するのでしばらくモード変更flgが立ち続けてしまうため。
        DrawBG = false;
        var ModeIsDestination = false;
        // 描画関数 updateですね
        processing.draw = function(){
            ModeIsDestination = Math.floor((new Date()/1000)) % 10  == 0;
            if(ModeIsDestination){
                var DSTS = DESTINATION[Math.floor(Math.random()*DESTINATION.length)];
                var transition = TRANSITIONS_DESTINATION[Math.floor(Math.random() * TRANSITIONS_DESTINATION.length)];
            }
            if(!DrawBG){
                processing.background(BG.r,BG.g,BG.b);
            }
            for(var i=0;i<PARTICLE_NUM;i++){
                if(ModeIsDestination){
                    // 指定場所モードになった暁にはトランジションをすべて
                    // 指定場所モードに変更して指定場所をセットしてcounterをFPS * 3,つまり3秒にする
                    // 状態管理用のsetflgも初期化する。
                    DrawBG = true;
                    PARTICLE_POOL[i].transition = transition;
                    PARTICLE_POOL[i].dst_x = DSTS[i % DSTS.length][0];
                    PARTICLE_POOL[i].dst_y = DSTS[i % DSTS.length][1];
                    PARTICLE_POOL[i].counter = DESTINATIONFLG.finish;
                    PARTICLE_POOL[i].setflg = DESTINATIONFLG.initial;
                }
                PARTICLE_POOL[i].draw();
            }
        };
    };

    var canvas = document.getElementById("sketch");
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    // attaching the sketchProc function to the canvas
    var processingInstance = new Processing(canvas, sketchProc);
}

$(function(){
      do_processing();
  });