xbd
xbd

Reputation: 975

How do I adjust the speed of an animation?

I want to create a bouncing ball in which I can adjust the speed it bounces

Here is my original code

 <script>

      var context;
      var x=100;
      var y=200;
      var dx=5;
      var dy=5;


      function init()
      {
         context= myCanvas.getContext('2d');
         setInterval(draw,100);
      }


      function draw()
      {
        context.clearRect(0,0, 300,300);
        context.beginPath();
        context.fillStyle="#B76EB8";
        // Draws a circle of radius 20 at the coordinates 100,100 on the canvas
        context.arc(x,y,20,0,Math.PI*2,true);
        context.closePath();
        context.fill();
        if( (x-20)<0 || (x+20)>300) dx=-dx; 
        if( (y-20)<0 || (y+20)>300) dy=-dy; 
        x+=dx; 
        y+=dy;

  }
</script>

However I found that the animation lags, when I decrease the ball speed via setInterval

So I used

  function init()
  {
    context= myCanvas.getContext('2d');
    (function animloop(){

      requestAnimFrame(animloop);
      draw();
      setTimeout(animLoop, 1);

    })();

  }

to make the animation smoother

But now I can't adjust the speed of the bouncing ball at all, are there other suggested ways I can achieve my goal?

Thanks

Upvotes: 0

Views: 2820

Answers (2)

markE
markE

Reputation: 105015

If you want less "jitter" at the expense of slower movement in your animation then set dx & dy ==1.

That way the user views the ball move in small increments rather than "jumps".

You might consider using requestAnimationFrame to run your animation loop. It gives you a timestamp which you can use to throttle your animation to various frames-per-second:

Here's example code: http://jsfiddle.net/m1erickson/Y9bYv/

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" />
<script src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; }
    canvas{border:1px solid red;}
</style>

<script>
    $(function(){

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

        var x=100;
        var y=200;
        var dx=1;
        var dy=1;

        var start;
        var fps=30;
        var delay=1000/fps;

        requestAnimationFrame(animate);;

        function draw(){
          context.clearRect(0,0, 300,300);
          context.beginPath();
          context.fillStyle="#B76EB8";
          // Draws a circle of radius 20 at the coordinates 100,100 on the canvas
          context.arc(x,y,20,0,Math.PI*2,true);
          context.closePath();
          context.fill();
        }


        function animate(time) {

            requestAnimationFrame(animate);

            if(start===null){start=time;}

            var elapsed=time-start;

            if(elapsed<delay){return;}

            draw();

            start=time;

            x+=dx; 
            y+=dy;

            if(x<20){ dx=-dx; x=20; }
            if(x>280){ dx=-dx; x=280; }
            if(y<20){ dy=-dy; y=20; }
            if(y>280){ dy=-dy; y=280; }
        }

    }); // end $(function(){});
</script>
</head>
<body>
    <canvas id="canvas" width=350 height=350></canvas>
</body>
</html>

Upvotes: 1

user2864740
user2864740

Reputation: 61875

The timeout/interval should be no less than say about 20ms and the animation function should animate based on the delta-time elapsed. This will result in a consistent animation (a discrete position function can be even better, but a simple delta-time approach often works well enough).

Then the overall ball speed can be controlled over (and independently of) any interval amount. Remember the formula: d = d0 + v * t. The original code is missing the t (delta-time) component and x+=dx is effectively equivalent to x = x0 + dx * 1. However, the time between intervals need not be constant and should be compensated for with a bit of math.

Consider this demo:

var interval=20;
var x=100;
var y=200;
var dx=100/1000; // px/1000millis
var dy=100/1000;

var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var w=canvas.getAttribute("width") || 0;
var h=canvas.getAttribute("height") || 0;

var prevTime = +(new Date)
function draw()
{
    var now = +(new Date)
    var delta = now - prevTime;
    if (delta < 5) {
       // Timeout occured too fast, we could be "stacking"
       // setInterval events, abort!
       return;
    }
    prevTime = now;
    // We now have "delta time millis" to get work done

    // Do movement before draw
    // (This bound check can actually get the
    //  ball stuck, but that's a different question!)
    if( (x-20)<0 || (x+20)>w ) dx = -dx;
    if( (y-20)<0 || (y+20)>h ) dy = -dy; 
    // Apply delta time
    x = x + dx * delta;
    y = y + dy * delta;

    context.clearRect(0, 0, w, h);

    context.beginPath();
    context.fillStyle="#B76EB8";
    context.arc(x,y,20,0,Math.PI*2,true);
    context.closePath();
    context.fill();

}

// Uset setInterval because it will "reprime" the next timeout
// callback faster and more consistently.
setInterval(draw, interval);

Note that changing the interval delay will make the animation smoother, but it will not affect the overall ball movement speed (this is a bit of a lie due to how the edge collision is handled). Try changing the interval from 20 to 10, 50, 100 and observe the result.

Upvotes: 1

Related Questions