Last active
December 18, 2015 13:39
-
-
Save williamcotton/5791703 to your computer and use it in GitHub Desktop.
RunLoop
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
RunLoop(document.querySelector("canvas"), function(context, tick) { | |
context.fillRect(Math.sin(tick/5)*20 + 100, 20, 40, 40); | |
}).play(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// requestAnimationFrame polyfill | |
window.requestAnimationFrame = (function(callback) { | |
return window.requestAnimationFrame || | |
window.webkitRequestAnimationFrame || | |
window.mozRequestAnimationFrame || | |
window.oRequestAnimationFrame || | |
window.msRequestAnimationFrame || | |
function(callback) { | |
window.setTimeout(function() { | |
var timestamp = Date.now(); | |
callback(timestamp); | |
}, 1000 / 60); | |
}; | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var RunLoop = function(context_or_canvas, callback) { | |
var context; | |
if (context_or_canvas.getContext) { | |
var canvas = context_or_canvas; | |
context = canvas.getContext('2d'); | |
var width = canvas.offsetWidth; | |
var height = canvas.offsetHeight; | |
context.width = width; | |
context.height = height; | |
} | |
else { | |
context = context_or_canvas; | |
} | |
var tick = 0; | |
var tickscale = 1; | |
var paused = false; | |
var scheduledEvents = []; | |
/* -- Out raison d'être... -- */ | |
var rl = function() { | |
// tick, tock | |
tick += tickscale; | |
// clear the canvas every frame | |
context.clearRect(0, 0, context.width, context.height); | |
// run callbacks scheduled with setInterval and setTimeout | |
runScheduledEvents(context, tick); | |
// run | |
callback(context, tick); | |
// loop | |
window.requestAnimationFrame(function(timestamp) { | |
if (!paused) { | |
rl(timestamp); | |
} | |
}); | |
} | |
/* -- Our event scheduler -- */ | |
var runScheduledEvents = function(context, tick) { | |
var previousEvents = scheduledEvents; | |
scheduledEvents.forEach(function(scheduledEvent) { | |
if (tick >= scheduledEvent.fire_at) { | |
scheduledEvent.callback(context, tick); | |
if (scheduledEvent.repeat) { | |
scheduledEvent.fire_at += scheduledEvent.elapsed_ticks; | |
} | |
else { | |
scheduledEvent.finished = true; | |
} | |
} | |
}); | |
scheduledEvents.forEach(function(scheduledEvent) { | |
if (scheduledEvent.finished) { | |
scheduledEvents.splice(scheduledEvents.indexOf(scheduledEvent), 1); | |
} | |
}); | |
} | |
/* -- Our module exports -- */ | |
var exports = { | |
getTickscale: function() { | |
return tickscale; | |
}, | |
setTickscale: function(new_tickscale) { | |
tickscale = new_tickscale; | |
}, | |
getTick: function() { | |
return tick; | |
}, | |
setTick: function(new_ticks) { | |
ticks = new_ticks; | |
}, | |
pause: function() { | |
paused = true; | |
}, | |
play: function() { | |
paused = false; | |
rl(); | |
}, | |
step: function(count) { | |
exports.pause(); | |
var c = count || 1; | |
for (var i=0; i < c; i++) { | |
rl(); | |
}; | |
}, | |
/* | |
Browser vendors have implemented power saving techniques related to the | |
animationFrame callback that cause the loop to pause when the browser tab | |
is not visible. This causes issues if window.setInterval is used to feed | |
events in to the run loop... when you come back to the browser tab with | |
your animation on it, all of the window.setInterval events are triggered | |
at once. | |
By creating our own setInterval mechanism we can alleviate the issue by | |
triggering the callbacks from within the context of the run loop. | |
*/ | |
setInterval: function(callback, ticks) { | |
exports.setTimeout(function() { | |
callback(); | |
}, ticks, true) | |
}, | |
setTimeout: function(callback, elapsed_ticks, repeat) { | |
scheduledEvents.push({ | |
callback: callback, | |
elapsed_ticks: elapsed_ticks, | |
fire_at: elapsed_ticks + tick, | |
repeat: repeat | |
}); | |
} | |
} | |
return exports; | |
}; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment