Animitter is a combination of an EventEmitter and a feature-filled animation loop. It uses requestAnimationFrame with an automatic fallback to setTimeout
and offers several additional features, such as framerate throttling, easy starting and stopping of the loop, providing deltaTime
in milliseconds between frames, as well as total elapsedTime
and frameCount
. Listen to the built-in update
, start
, stop
, complete
and reset
events, or emit your own.
npm install animitter --save
copy ./animitter.js
or ./animitter.min.js
into your project
<script src="js/animitter.js"></script>
or with require.js/amd:
require(['animitter'], function( animitter ){});
var loop = animitter(function(deltaTime, elapsedTime, frameCount){
//do something
}).start();
var loop = animitter(function(deltaTime, elapsedTime, frameCount){
//do something
});
loop.on('start', function(deltaTime, elapsedTime, frameCount){
//loop started
});
loop.on('update', function(deltaTime, elapsedTime, frameCount){
if( frameCount === 100 ){
//`this` is scoped to the Animitter instance
this.complete();
}
});
loop.on('stop', function(deltaTime, elapsedTime, frameCount){
//this will get triggered on a `complete` also
});
loop.on('complete', function(deltaTime, elapsedTime, frameCount){
//done, can't be started again unless reset
});
loop.on('reset', function(deltaTime, elapsedTime, frameCount){
});
loop.start();
Animitter provides animitter().start()
and animitter().stop()
to easily pause and resume an animation. Any stopped loop can be resumed with animitter().start()
as long as it has not been marked as completed via animitter().complete()
.
var stats = document.createElement('div');
var button = document.createElement('button');
button.innerHTML = 'play';
var loop = animitter(function(deltaTime, elapsedTime, frameCount){
stats.innerHTML = Math.floor(this.getFPS()) + 'fps, elapsed: ' +elapsedTime+ ', delta: ' +deltaTime+ ' frames: ' +frameCount;
});
loop.on('start', function(){ button.innerHTML = 'pause'; });
loop.on('stop', function(){ button.innerHTML = 'play'; });
button.addEventListener('click', function(){
if(loop.isRunning()){
loop.stop();
} else {
loop.start();
}
});
document.body.appendChild(stats);
document.body.appendChild(button);
Animitter uses the same EventEmitter as Node.js. This allows you to emit
events, add listeners with on
,once
,addListener
or remove listeners with removeListener
or removeAllListeners
.
The following example periodically emits a custom event. A listener is added for the event which removes itself after a few uses:
var timesDovesHaveFlown = 0,
shouldMakeDovesFly = function(){ return Math.random() > 0.9; };
var loop = animitter(function(deltaTime, elapsedTime, frameCount){
//play an animation
if( shouldMakeDovesFly() ){
//after the event-type, pass any parameters you want the listener to receive
this.emit('doves-fly', doves);
}
});
loop.on('doves-fly', function(doves){
timesDovesHaveFlown++;
//make doves fly here
if( timesDovesHaveFlown > 4 ){
this.removeListener('doves-fly', makeDovesFly);
}
});
loop.start();
Animitter inherits from EventEmitter which provides methods such as emit
, on
, removeListener
etc… below (in alphabetical order) are the methods that animitter provides directly:
- options.fps : Number defaults to Infinity, set a framerate to throttle the loop, otherwise it will run as fast as possible, 60fps on desktop, 60-100fps on VRDisplay
- options.requestAnimationFrameObject : Object defaults to global object, allows providing in an object such as a VRDisplay to use its
requestAnimationFrame
related methods. - options.delay : Number defaults to 0, set an optional delay in milliseconds to wait before invoking the first update on the loop after calling
start()
- options.fixedDelta : Boolean defaults to false, if true,
'update'
events will always reportdeltaTime
andelapsedTime
as consistent framerate intervals. Useful in situations such as when you may be recording output at a rate different than real-time.
stop the loop and mark it as completed and unable to start again.
stop the loop and remove all listeners.
returns the calculated rate of frames-per-second for the last update based on animitter().getDeltaTime()
, will return 0
if the loop has not started yet.
return the framerate as set via options.fps
or animitter().setFPS(fps)
, returns Infinity
if no limit has been set.
returns the object that is providing requestAnimationFrame
to this instance
return the time between the last two frames in milliseconds
return the sum of all elapsed time while running in milliseconds. This is the sum of deltas between frames; if a loop is stopped and started later that time will not be included as elapsed time.
return the number of times the loop has repeated.
return true if the loop is currently active
return true if the loop has been marked as completed.
stops the loop and resets its times, frameCount and whether it was completed, leaves listeners intact.
throttle the loop to a preferred framerate between 0-60.
allows you to use requestAnimationFrame
from a custom object, such as a VRDisplay.
Below is an example of using animitter().setRequestAnimationFrameObject
with WebVR:
var loop = animitter().start();
navigator.getVRDisplays().then((displays)=>{
if(displays.length > 0){
var vrDisplay = displays[0];
loop.setRequestAnimationFrameObject(vrDisplay)
loop.on('update', function(){
//now you can safely call VRDisplay#submitFrame()
vrDisplay.submitFrame();
});
}
});
starts repeating the update loop.
stops repeating the update loop.
updates the loop once.
The animitter
object comes with the property running
this counter indicates the number
of animitter instances that are currently animating. This can be helpful for debugging to ensure
that you are properly stopping all of your animitter instances.
Setting this to true will force all animitter instances to behave as if options.fixedDelta
was true
. A helpful application-wide toggle if you begin to record the output not in real-time.
Animitter comes with TAP tests. These tests are compatible in node and in browser. To run the tests in node:
npm test
To run tests in browser: A very simple way of accomplishing this, is to use something like budo and watch your console for logs:
npm install budo -g
budo test.js
animitter().getFPS()
now returns the actual running frame rate instead of just returning the frame rate set viaanimitter().setFPS(fps)
, for this behavior useanimitter().setFPSLimit()
.animitter().getElapsedTime()
no longer includes durations that elapsed in-between when a loop has been stopped and started again.
In v1.0.0 a few aspects of the API have changed. Most notably the parameter signature of all events is now:
function onEvent( deltaTime, elapsedTime, frameCount ){ }
Additionally, animitter().isAnimating()
has been renamed to isRunning()
and { async: true }
option no longer is available. Instead users should call animitter().update()
directly to update their loop asynchronously.
MIT License