|
/** |
|
* A polyfill to get the current timestamp. |
|
* window.performance.now will fallback to using |
|
* Date.now or new Date().getTime() on unsupported browsers. |
|
* Author: Anthony Del Ciotto: http://anthonydel.com |
|
*/ |
|
(function(window) { |
|
"use strict"; |
|
if (typeof window.performance.now === "undefined") { |
|
window.performance.now = (typeof Date.now === "undefined") ? |
|
new Date().getTime() : Date.now(); |
|
} |
|
})( window ); |
|
|
|
// http://paulirish.com/2011/requestanimationframe-for-smart-animating/ |
|
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating |
|
// requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel |
|
// MIT license |
|
(function(window) { |
|
var lastTime = 0; |
|
var vendors = ['ms', 'moz', 'webkit', 'o']; |
|
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { |
|
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; |
|
window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] |
|
|| window[vendors[x]+'CancelRequestAnimationFrame']; |
|
} |
|
|
|
if (!window.requestAnimationFrame) |
|
window.requestAnimationFrame = function(callback, element) { |
|
var currTime = new Date().getTime(); |
|
var timeToCall = Math.max(0, 16 - (currTime - lastTime)); |
|
var id = window.setTimeout(function() { callback(currTime + timeToCall); }, |
|
timeToCall); |
|
lastTime = currTime + timeToCall; |
|
return id; |
|
}; |
|
|
|
if (!window.cancelAnimationFrame) |
|
window.cancelAnimationFrame = function(id) { |
|
clearTimeout(id); |
|
}; |
|
}( window )); |
|
|
|
|
|
(function(window) { |
|
"use strict"; |
|
|
|
var canvas = document.getElementById("game-canvas"); |
|
var ctx = canvas.getContext("2d"); |
|
var canvasWidth = canvas.width; |
|
var canvasHeight = canvas.height; |
|
|
|
var fps = 30; |
|
var frameDuration = 1000 / fps; |
|
var lag = 0; |
|
var previous = 0; |
|
var startTime = 0; |
|
|
|
/** |
|
* just a simple sprite object for demonstration purposes |
|
*/ |
|
var sprite = { |
|
x: canvasWidth / 2, |
|
y: canvasHeight / 2, |
|
displayX: 0, |
|
displayY: 0, |
|
amplitude: 150, |
|
period: 2000, |
|
width: 32, |
|
height: 32, |
|
|
|
update: function() { |
|
// get the elapsed time since we started |
|
var time = window.performance.now() - startTime; |
|
|
|
// perform an oscillation animation |
|
var centerX = canvasWidth / 2 - this.width / 2; |
|
this.x = this.amplitude * Math.sin(time * 2 * Math.PI / this.period) + centerX; |
|
}, |
|
|
|
interpolate: function(lagOffset) { |
|
// use the lagOffset and the sprites previous x/y pos to interpolate |
|
// the position |
|
this.displayX = (typeof this.previousX !== "undefined") ? |
|
(this.x - this.previousX) * lagOffset + this.previousX : this.x; |
|
this.displayY = (typeof this.previousY !== "undefined") ? |
|
(this.y - this.previousY) * lagOffset + this.previousY : this.y; |
|
}, |
|
|
|
display: function(lagOffset) { |
|
this.interpolate(lagOffset); |
|
|
|
// draw the sprite bro |
|
ctx.fillStyle = '#ffa700'; |
|
ctx.translate(this.displayX + (this.width / 2), this.displayY + (this.height / 2)) |
|
ctx.fillRect(-this.width / 2, -this.height / 2, this.width, this.height); |
|
} |
|
}; |
|
|
|
var update = function() { |
|
sprite.update(); |
|
}; |
|
|
|
var display = function(lagOffset) { |
|
ctx.fillStyle = '#FFFFFF'; |
|
ctx.fillRect(0, 0, canvasWidth, canvasHeight); |
|
ctx.save(); |
|
sprite.display(lagOffset); |
|
ctx.restore(); |
|
}; |
|
|
|
var gameLoop = function() { |
|
requestAnimationFrame(gameLoop); |
|
|
|
// calculate the delta or elapsed time since the last frame |
|
var now = window.performance.now(); |
|
var delta = now - previous; |
|
|
|
// correct any unexpected huge gaps in the delta time |
|
if (delta > 1000) { |
|
delta = frameDuration; |
|
} |
|
|
|
// accumulate the lag counter |
|
lag += delta; |
|
|
|
// perform an update if the lag counter exceeds or is equal to |
|
// the frame duration. |
|
// this means we are updating at a Fixed time-step. |
|
if (lag >= frameDuration) { |
|
// capture sprites previous frame position |
|
sprite.previousX = sprite.x; |
|
sprite.previousY = sprite.y; |
|
|
|
// update the game logic |
|
update(); |
|
|
|
// reduce the lag counter by the frame duration |
|
lag -= frameDuration; |
|
} |
|
|
|
// calculate the lag offset, this tells us how far we are |
|
// into the next frame |
|
var lagOffset = lag / frameDuration; |
|
|
|
// display the sprites passing in the lagOffset to interpolate the |
|
// sprites positions |
|
display(lagOffset); |
|
|
|
// set the current time to be used as the previous |
|
// for the next frame |
|
previous = now; |
|
}; |
|
|
|
startTime = window.performance.now(); |
|
gameLoop(); |
|
|
|
})( window ); |