HTML5 Context / Canvas - когда вызывать draw в контексте - PullRequest
2 голосов
/ 09 февраля 2012

Я пишу небольшую демонстрацию javasscript в объектно-ориентированном стиле - просто чтобы нарисовать кучу шариков, движущихся по экрану. ничего необычного, никакого обнаружения столкновений или чего-либо еще Считай, что мой класс Ball.js хорош.

Мой вопрос сводится к следующему: где я должен назвать ball.draw (context)? Кажется, что единственный способ получить шары на экране, как я их настроил, - это сделать вызов в generateBalls (). Но это означает, что каждый мяч разыгрывается один раз.

Так что я действительно оценил бы это, если бы кто-то мог указать на ошибку моих путей здесь. Это не домашнее задание - просто попытаться лучше понять javascript и canvas.

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Untitled Document</title>
<script src="ball.js"></script>
<script src="utils.js"></script>
...
    <canvas id="canvas" width="600" height="480"></canvas>
    <script type="text/javascript">
        window.addEventListener('load', eventWindowLoaded, false);

        function eventWindowLoaded() {
            canvasApp();    
        }

        function canvasSupport() {
            return true;    
        }

        function canvasApp() {
            if(!canvasSupport()) {
                return; 
            }
        }
        console.log("app entered");
        var numBalls = 45;
        //var numBalls = demo.numberofballs.value;
        var maxSize = 8;
        var minSize = 5; 
        var maxSpeed = maxSize + 5;
        var balls = new Array();
        var tempBall;
        var tempX;
        var tempY;
        var tempSpeed;
        var tempAngle;
        var tempRadius;
        var tempRadians;
        var tempXunits;
        var tempYunits;

        canvas = document.getElementById("canvas");
        context = canvas.getContext("2d");

        generateBalls();

        setInterval(drawScreen, 33);

        function generateBalls() {
            console.log("Make some balls");
            for(var index = 0; index < numBalls; index++) {
                var tempRadius = Math.floor(Math.random()*maxSize)+minSize;
                var ball = new Ball(tempRadius, "#000000"); 
                ball.x = tempRadius * 2 + (Math.floor(Math.random()*canvas.width) -  tempRadius * 2);
                ball.y = tempRadius * 2 + (Math.floor(Math.random()*canvas.height) - tempRadius * 2);
                ball.speed = maxSpeed - tempRadius;
                ball.angle = Math.floor(Math.random()*360);
                ball.dx = Math.cos(tempRadians) * tempSpeed;
                ball.dy = Math.sin(tempRadians) * tempSpeed;
                // here outputted balls but a stupid place to put it LOL
                balls.push(ball);



            }

        }

        function drawScreen() {
            console.log("draw screen");



            // loop through all balls and adjust their position
            // a BallManager could do this more cleanly
            for(var index = 0; index < balls.length; index++) {

                context.fillStyle="#EE00EE";
                context.fillRect(0,0,canvas.width, canvas.height);

                // Box
                context.strokeStyle = "#ff0043";
                context.strokeRect(1,1,canvas.width-2, canvas.height-2);

                // place balls
                context.fillStyle = "#ff8783";
                console.log("ball mover loop in drawscreen");
                // no var ball now

                ball = balls[index];
                ball.x += ball.dx;
                ball.y += ball.dy;
                ball.draw(context);
                //checkBoundaries(balls[index]);
                if(ball.x > canvas.width || ball.x < 0) {
                ball.angle = 180 - ball.angle;
                updateBall(ball);   
                    } else if(ball.y > canvas.height || ball.y < 0) {
                        ball.angle = 360 - ball.angle;
                        updateBall(ball);   
                        //ball.draw(context);
                }

            }

        }   

        //function checkBoundaries(ball) {
            //console.log("Check Bounds: " + " " + "ball.x: " + ball.x + " " + //"ball.y: " + ball.y);

        //}

        function updateBall(ball) {
            ball.radians = ball.angle * Math.PI / 180;
            ball.dx = Math.cos(ball.radians) * ball.speed;
            ball.dy = Math.sin(ball.radians) * ball.speed;
            //ball.draw(context);
        }

    </script>
</body>
</html>

Спасибо за ваш совет, Marc

Ответы [ 2 ]

2 голосов
/ 09 февраля 2012

Ваш пример содержит более одной ошибки, пожалуйста, проверьте ваш измененный код. Это работает, но вы должны расширить и исправить его.

<!DOCTYPE HTML>
<html>
  <head>
  <meta charset="utf-8">
  <title>Untitled Document</title>
  <script type="text/javascript">
    // next lines is a Ball() implementation code
    Ball = function(radius,color) {
        this.radius=radius;
        this.color=color;
    };
    Ball.prototype.x=0;
    Ball.prototype.y=0;
    Ball.prototype.speed=0;
    Ball.prototype.angle=0;
    Ball.prototype.dx=0;
    Ball.prototype.dy=0;
    Ball.prototype.radius=10;
    Ball.prototype.color="#000";
    Ball.prototype.draw=function() {

        context.beginPath();
        context.arc(this.x,this.y,this.radius,0,Math.PI*2,true);
        context.lineWidth = 5;
        context.strokeStyle = this.color; // line color
        context.stroke();
        context.closePath();
    };

    window.addEventListener('load', eventWindowLoaded, false);

    function eventWindowLoaded() {
        canvasApp();    

        //console.log("app entered");
        window.canvas = document.getElementById("canvas");
        window.context = canvas.getContext("2d");

        generateBalls();
        // if you want to use setInterval() instead replace next line
        setTimeout(drawScreen, 33);
    }

    function canvasSupport() {
        return true;    
    }

    function canvasApp() {
        if(!canvasSupport()) {
            return;
        }
    }

    var numBalls = 45;
    //var numBalls = demo.numberofballs.value;
    var maxSize = 8;
    var minSize = 5;
    var maxSpeed = maxSize + 5;
    var balls = new Array();
    var tempBall;
    var tempX;
    var tempY;
    var tempSpeed;
    var tempAngle;
    var tempRadius;
    var tempRadians;
    var tempXunits;
    var tempYunits;

    function generateBalls() {
        //console.log("Make some balls");
        for(var index = 0; index < numBalls; index++) {
            var tempRadius = Math.floor(Math.random()*maxSize)+minSize;
            var tempRadians = Math.random()*Math.PI;
            var tempSpeed = 10;
            var ball = new Ball(tempRadius, "#000000");
            ball.x = tempRadius * 2 + (Math.floor(Math.random()*canvas.width) -  tempRadius * 2);
            ball.y = tempRadius * 2 + (Math.floor(Math.random()*canvas.height) - tempRadius * 2);
            ball.speed = maxSpeed - tempRadius;
            ball.angle = Math.floor(Math.random()*360);
            ball.dx = Math.cos(tempRadians) * tempSpeed;
            ball.dy = Math.sin(tempRadians) * tempSpeed;
            // here outputted balls but a stupid place to put it LOL
            balls.push(ball);
        }
    }

    function drawScreen() {
        console.log("draw screen");


        context.fillStyle="#EE00EE";
        context.fillRect(0,0,canvas.width, canvas.height);

        // Box
        context.strokeStyle = "#ff0043";
        context.strokeRect(1,1,canvas.width-2, canvas.height-2);

        // loop through all balls and adjust their position
        // a BallManager could do this more cleanly
        for(var index = 0; index < balls.length; index++) {
            // place balls
            context.fillStyle = "#008700";
            //console.log("ball mover loop in drawscreen");
            // no var ball now

            ball = balls[index];
            ball.x += ball.dx;
            ball.y += ball.dy;
            ball.draw(context);
            //checkBoundaries(balls[index]);
            if(ball.x > canvas.width || ball.x < 0) {
                ball.angle = 180 - ball.angle;
                updateBall(ball);   
            } else if(ball.y > canvas.height || ball.y < 0) {
                ball.angle = 360 - ball.angle;
                 updateBall(ball);   
                //ball.draw(context);
            }

        }
        // if you want to use setInterval() instead remove next line
        setTimeout(drawScreen, 33);
    }   

    //function checkBoundaries(ball) {
        //console.log("Check Bounds: " + " " + "ball.x: " + ball.x + " " + //"ball.y: " + ball.y);

    //}

    function updateBall(ball) {
        ball.radians = ball.angle * Math.PI / 180;
        ball.dx = Math.cos(ball.radians) * ball.speed;
        ball.dy = Math.sin(ball.radians) * ball.speed;
        //ball.draw(context);
    }

  </script>
  </head>
  <body>
    <canvas id="canvas" width="600" height="480" style="background:red;"></canvas>
  </body>
</html>

http://jsfiddle.net/QVgZx/2/

1 голос
/ 29 июня 2013

Просто добавьте в ветку поток, чтобы добавить немного обновления для любого новичка в canvas, который прибывает сюда:

Стэн рекомендовал создать функцию для рисования каждого из шаров в одном месте кода;это хорошо по ряду причин, это делает анимацию более плавной, а код гораздо легче отлаживать / поддерживать.

Я бы не согласился со Стэном, когда дело доходит до использования setInterval для его запуска ..хотя, возможно, это было обычной практикой, когда Стэн написал это.

Теперь, однако, мы используем requestAnimationFrame.

Это синхронизирует вашу картину с остальной частью браузера.

Это позволяет вашему коду работать намного быстрее, так как каждый раз, когда браузер что-то рисует, ему приходится снова просматривать всю страницу, чтобы определить, куда все пойдет, на случай, если что-то переместится.

Использование setInterval не оставляет никаких гарантий относительно того, когда он будет запущен, и, конечно, не время для его совпадения с обновлением экрана браузера.

Это означает, что он немного рисует, реконфигурирует страницу, рисует немного больше,перенастраиваем снова, рисуем еще ... и снова и снова.Это очень плохо и очень медленно.

Использование requestAnimationFrame, однако, позволяет браузеру вызывать вашу функцию, когда он все равно собирается перерисовать экран ... что означает единственную перерисовку и одно обновление.

Гораздо быстрее, намного чище.

Работает немного по-другому, но на самом деле очень просто.

requestAnimationFrame(redraw);

Это регистрирует вашу функцию 'перерисовать' с помощью requestAnimationFrame, так чтов следующий раз, когда браузер захочет перерисовать окно, будет вызвана ваша функция.

Единственное, с чем можно столкнуться, это то, что он будет вызывать вашу функцию только ОДНАЖДЫ ... таким образом, это похоже на setTimeout, а неa setInterval.

Таким образом, вам не нужно передавать переменную таймера, чтобы остановить анимацию;он остановится, потому что он перестал вызываться.

Однако, чтобы гарантировать, что ваша анимация продолжает работать, просто поместите тот же самый вызов внизу функции перерисовки:

function redraw()
{
    var blah, blahblah;
    ...

    requestAnimationFrame(redraw);
}

Youможно также установить это условно, чтобы запустить анимацию до ее завершения, а затем остановить:

function redraw()
{
    ...

    if (!finished) requestAnimationFrame(redraw);
}

ссылка Mozilla ЗДЕСЬ , Пол Ирландский ЗДЕСЬ и Крис Койер ЗДЕСЬ .

...