<!doctype html>
<html>
<head>
<style>
canvas { border:1px solid black; width:480px; height:320px; }
</style>
</head>
<body>
<canvas id='screen' width='480' height='320'></canvas>
<p><input type="checkbox" id="sync">Sync update to animate</input>
<p>Step delay (ms):<input type="text" id="stepdelay"></input>
<p>Animate delay (ms):<input type="text" id="animatedelay"></input>
<p><input type="text" id="counter"></input>
<script>
// Grab the canvas
var canvas = document.getElementById('screen'),
ctx = canvas.getContext('2d'),
gamewidth = canvas.width,
gameheight = canvas.height,
running = false,
sync = document.getElementById("sync"),
timer = {
fps: 1/60, // Frames per second
fpsMS: 1000/60, // Frame time in ms
accum:0, // Accumulated deltas
delta:0, // the delta between frames
last:(new Date()).getTime(),
now: (new Date()).getTime(),
// tick the clock on each update (simulation step)
tick: function() {
this.last = this.now;
this.now = (new Date()).getTime();
this.delta = Math.min(this.now - this.last, 250);
this.accum += this.delta;
}
},
// Keep track of the actual updates per second (ups) and frames per second (fps)
counter = document.getElementById("counter"),
laststep = Date.now(),
lastanimate = Date.now(),
avgdtu = 0,
avgdtf = 0,
updatecounter = function () {
counter.value = (1000/avgdtu).toPrecision(3) + "ups " + (1000/avgdtf).toPrecision(3) + "fps";
},
// Call this once per step to keep count
countstep = function () {
var now = Date.now(), dt = now - laststep;
laststep = now;
avgdtu = 0.95 * avgdtu + 0.05 * dt;
updatecounter();
},
// Call this once per animate to keep count
countanimate = function () {
var now = Date.now(), dt = now - lastanimate;
lastanimate = now;
avgdtf = 0.95 * avgdtf + 0.05 * dt;
updatecounter();
},
// Do nothing for dt milliseconds
delay = function (dt) {
var end = Date.now () + dt;
while (Date.now() < end);
},
stepdelay = document.getElementById("stepdelay"),
animatedelay = document.getElementById("animatedelay"),
// A simple box to bounce around the canvas
box = {
position: { x:228, y:135},
lastPostition: { x:228, y:135},
velocity: { x1:1, x2:1},
speed: 60,
width:16, height:16
},
/*
** This is the simulation step
*/
pendingUpdate = null;
update = function() {
// ticks the clock,
timer.tick();
// steps the simulation at a fixed delta until the accumlated delta time is consumed
while(timer.accum >= timer.fpsMS) {
timer.accum -= timer.fpsMS;
step(timer.fps);
}
// if running continue the simulation loop
if(running && !sync.checked) { pendingUpdate = setTimeout(update, timer.fpsMS); }
else pendingUpdate = null;
},
step = function(delta) {
countstep();
delay(+stepdelay.value);
// move the old position to lastPosition before updating to preserve two states
box.lastPostition = box.position;
// compute the change in (x, y)
var dx = box.velocity.x1 * box.speed * delta,
dy = box.velocity.x2 * box.speed * delta;
// if out of bounds reverse velocity, otherwise add the change in position
if(box.position.x + dx < 0 || box.position.x + dx + box.width > gamewidth) {
box.velocity.x1 = box.velocity.x1 * -1;
} else {
box.position.x += dx;
}
if(box.position.y + dy < 0 || box.position.y + dy + box.height > gameheight) {
box.velocity.x2 = box.velocity.x2 * -1;
} else {
box.position.y += dy;
}
},
/*
** This is the animation step
*/
animate = function() {
countanimate();
delay(+animatedelay.value);
if (!pendingUpdate) update();
// If running request a new frame continuing the loop
if(running) { window.requestAnimFrame(animate); }
// Linear Interpolation
var a = timer.accum > timer.fspMS ? timer.accum / timer.fpsMS : 1,
x0 = box.lastPostition.x, y0 = box.lastPostition.y,
x1 = box.position.x, y1 = box.position.y,
pt = {
x: x0 + ( (x1 - x0) * a ),
y: y0 + ( (y1 - y0) * a )
};
// Canvas clear and drawing
ctx.clearRect(0, 0, gamewidth, gameheight);
ctx.fillRect(pt.x, pt.y, box.width, box.height);
},
// Called on load to start the loops
start = function() {
running = true;
update();
animate();
};
window.onload = start;
// Awesome requestAnimationFrame shim by Paul Irish
// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
</script>
</body>
</html>