Reputation: 318
I want to create a simple animation with gravity (http://jsfiddle.net/2pcr9/4/ tested in chrome and firefox). I'm doing that in Javascript with canvas. The gravity and speed is calculated with a variable timestep using (:46):
this.realDelta = (now - this.prevTime) / 1000;
this.realTime += this.realDelta;
And each ball has this code for calculation of speed and position (:148):
this.vy += this.gravity * time.delta;
this.x += this.vx * time.delta;
this.y += this.vy * time.delta;
Now, this prototype works without problems when you are viewing the window.
But if you hide the current window (switch to another tab of your browser), the window.requestAnimationFrame stops. If you wait 5 seconds, and open the window, the window.requestAnimationFrame resumes. The problem is that the realDelta is now 5 seconds and is making the velocity too big.
I can add a check to not allow realDelta be bigger than a second (:46):
this.realDelta = (now - this.prevTime) / 1000;
if (this.realDelta > 1) {
this.realDelta = 1;
}
this.realTime += this.realDelta;
but this doesn't really solve the issue.
Now I don't understand how to really fix this. Should I check if the window is hidden and pause the timer (and resume the timer when the window is visible again). Or is there an error in my calculation of the speed? How are other html5 games fixing this issue of 'dt' being too big?
Thanks
Upvotes: 1
Views: 572
Reputation: 105035
Use these jQuery events to start/stop your frame calculations:
(You can try without jQuery--straight javascript, but X-browser stuff will drive you crazy!)
$(window).blur(function(e) {
// User has changed tabs away from your window
// So freeze your incrementing until the user returns
});
$(window).focus(function(e) {
// User has returned to your tab
// So begin your incrementing again
});
The only gotcha is in some browsers these events get fired multiple times, so you might want to also set an on/off flag so you just react to the first firing.
Upvotes: 1
Reputation: 2814
Inside your draw function, you can compensate for missed frames by running your function multiple times to catch up:
var TIME_INTERVAL = 1000/60;
var timeElapsed = 0;
requestAnimFrame(draw, TIME_INTERVAL);
function draw() {
timeElapsed += now - this.prevTime;
this.prevTime = now;
while(timeElapsed > TIME_INTERVAL)
{
timeElapsed -= TIME_INTERVAL;
// Run Draw
}
requestAnimFrame(draw, TIME_INTERVAL);
}
Of course, if the user is tabbed for too long they can come back to a lot of computation. You can also consider running a backup in case RequestAnimFrame isn't available:
var TIME_INTERVAL = 1000/60;
var timeElapsed = 0;
var backup = setInterval(draw, TIME_INTERVAL*5);
requestAnimFrame(draw, TIME_INTERVAL);
function draw() {
clearInterval(backup);
timeElapsed += now - this.prevTime;
this.prevTime = now;
while(timeElapsed > TIME_INTERVAL)
{
timeElapsed -= TIME_INTERVAL;
// Run Draw
}
backup = setInterval(draw, TIME_INTERVAL*5);
requestAnimFrame(draw, TIME_INTERVAL);
}
Upvotes: 2