Reputation: 975
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
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
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