2014年11月3日月曜日

Canvasによる簡易アニメーションの実装。とりあえず定番のピンポンでも。

Canvasによる単純な図形の描画

前回の記事に引き続き、今回はCanvasを用いて簡単なアニメーションを表示してみます。
定番(?)のピンポン的なあれを、JavaScript的なオブジェクト指向(で良いのでしょうか。。。)で書いてみました。

ちなみにこれまでPythonやJavaを書く機会があったのですが、PythonやJavaScriptの紳士協定的なプライベート変数の扱い方でもそれほど問題を感じていません。私は先頭にアンダースコアをつけたい派です(今回は個人的なお絵かきなので省略)。Pythonから流れてきているので。

特にマシンの性能がばらばらというウェブ上で数字を求める時なんかは、クロージャを使ってメモリ圧迫する可能性を考慮しながら実装するのは骨が折れますし。

ただ、実装者のレベルがまちまちかつ、大規模開発環境だと、Javaのきっちり感はやはり有難かったですね。

さてさて、前回はonloadを用いて、ページ読み込み時に描画していましたが、今回はJQueryを利用してボタンをつけました。

Canvas

アニメーションのポイントは、描画処理を以下のようにsetIntervalで書いてあげることです。

timer = setInterval(draw, 10);

今回の処理をまとめると、おおよそ以下の動きとなります。

①クリックイベントが発生。

②ボールのオブジェクトを作成し、そこにサイズや速度などの情報を格納。それをランダム個作成
し、配列に格納。

③配列をループ処理し、それぞれの位置を変更。

やっていることはこれだけです。その際、clearRectを使って毎回画面をリセットする必要があります。

壁でバウンドする処理は、壁に到達した際に方向を変えることで表現しています。ボールの位置がキャンバスの幅をはみ出る値に達した場合、speed(速度と方向を意味する変数です)に-1をかけることで、逆向きにします。

if(this.locX < 0 || this.locX > canvasW){
this.speedX *= -1;
}

以下が今回のソースです。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Canvas</title>
    <style>
        canvas{
          border: solid 2px;
          background-color:rgba(0,0,0);
          margin-bottom: 5px;
        }
        button{
          margin-left: 5px;
          border-radius: 20px;
          background-color: rgba(127,181,224,0.5);
          width: 80px;
          height:30px;
          padding: 4px;
          font-size: 16px;
          text-shadow: 0, 1px, 0 rgba(0,0,0,0.5);
        }
    </style>
</head>
<body>
    <div class="container">
        <canvas id="anime" width=480 height=360></canvas>
        <div>
            <button id="play">Start</button>
            <button id="clear">Stop</button>
        </div>
    </div><!--container-->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script>
        var timer;
        var nowPlaying = false;
 
        var canvasW = 480;
        var canvasY = 360;
 
        var point = new Point(canvasW,canvasY);
 
        var ball_nums;
        var balls;
 
        $(function(){
            $('#play').click(function(){
                if (nowPlaying) return;
                init();
                nowPlaying = true;
                timer = setInterval(draw, 10);
            });
            $('#clear').click(function(){
                clearInterval(timer);
                init();
                clear();
                nowPlaying = false;
            });
        });
 
        //initialize the canvas
        function clear(){
            var canvas = document.getElementById("anime");
            //old browser does not support canvas. Avoid error.
            if (canvas.getContext){
                var context = canvas.getContext('2d');
                context.clearRect(0,0,canvasW,canvasY);
            }
        }
 
        function draw(){
            var canvas = document.getElementById("anime");
            //old browser does not support cavas. Avoid error.
            if (canvas.getContext){
                var context = canvas.getContext('2d');
                paint(context);
            }
        }
 
        //create ball objects
        function init(){
            ball_nums = Math.floor(Math.random() * 500);
            balls = [ball_nums];
            for(i = 0; i < ball_nums; i++){
                balls[i] = new Ball();
            }
        }
 
        function paint(context){
            //overwrite canvas
            context.globalCompositeOperation = "source-over";
 
            /* This makes canvas dark */
            //context.fillStyle = "rgba(10,10,8,0.2)";
            //context.fillRect(0, 0, canvasW, canvasY);
 
            //refresh the canvas
            context.clearRect(0,0,canvasW,canvasY);
 
            //This allows objects to merge, for example if there are blue and yellow,
            //it turns to be green.
            context.globalCompositeOperation = "lighter";
 
            for (i = 0; i < ball_nums; i++){
                balls[i].relocate();
 
                context.beginPath();
                context.fillStyle = balls[i].color;
                context.arc(balls[i].locX, balls[i].locY, balls[i].radius, 0, Math.PI*2.0, true);
                context.fill();
            }
        }
 
        //each ball object. locations change with being added each speed.
        function Ball(){
            this.speedX = Math.random() * 5;
            this.speedY = Math.random() * 5;
            this.locX = point.randomX();
            this.locY = point.randomY();
            this.color = randomColor();
            this.radius = Math.random() * 20;
        }
 
        Ball.prototype.relocate = function(){
            //relocate
            this.locX += this.speedX;
            this.locY += this.speedY;
 
            //to other way when this reaches a wall
            if(this.locX < 0 || this.locX > canvasW){
                this.speedX *= -1;
            }
            if(this.locY < 0 || this.locY > canvasY){
                this.speedY *= -1;
            }
        };
 
        function randomColor(){
            var r = Math.floor(Math.random() * 256);
            var g = Math.floor(Math.random() * 256);
            var b = Math.floor(Math.random() * 256);
            var a = Math.random();
            return 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
        }
 
        //For getting a random point on the canvas
        function Point(canvas_width, canvas_height){
            this.x = canvas_width;
            this.y = canvas_height;
        }
        Point.prototype.randomX = function(){
            return Math.random() * this.x;
        };
        Point.prototype.randomY = function(){
            return Math.random() * this.y;
        };
    </script>
  </body>
</html>

0 件のコメント:

コメントを投稿