Hi! I'm Nicolas and I'm interested in information visualization, JavaScript and web standards.
I currently work as a Data Visualization Scientist at Twitter. I wrote PhiloGL, the JavaScript InfoVis Toolkit and V8-GL.
JavaScript animations are a key aspect of dynamic Web Sites and Application development. Moreover, most JavaScript Frameworks or Libraries provide APIs for dealing with at least three main things:
Advanced DOM manipulation
Ajax
Animations
When developing Web Sites most JavaScript effects involve rendered DOM Elements, but sometimes JavaScript animations are used in other contexts, like when using the Canvas. In the JavaScript InfoVis Toolkit the main target of my animations are Graphs, and in the next version also Nodes and Edges as separate entities.
Today I'd like to describe how to create a generic animation class that can be used or extended for any purpose. I'll try to be minimalistic and to present only the needed code for making animations. Then you might find useful to add some code to perform specific animation tasks targeting for example specific style properties of a DOM Element.
Defining an Animation Class
Before creating an Animation class we might want to consider what to expose as options to the user. The options I thought of are:
The duration of the animation (in milliseconds)
The frames per second of the animation
Additionally we'd like to add a couple of controllers, one when a step of the animation is executed and one when the animation has completed:
delta gives us an idea of the progress of the animation. When the animation starts delta will be equal to zero. When the animation ends it'll be equal to one.
We will also need a start method to trigger the animation and a step method that will compute delta at each step.
Now that we defined our options we can start thinking about our implementation.
Implementing the Animation class
Our Animation class will be a class constructor that sets all the options and properties that we defined before and a prototype with the methods start and step. The class could be used like this:
varfx=newEffect({duration:1000,fps:40,onStep:function(delta){/* do stuff */},onComplete:function(){alert('done!');}});//start the animationfx.start();
Here's the code I came up with, inspired by the MooTools Framework:
//define the class constructorfunctionEffect(opt){this.opt={duration:opt.duration||1000,fps:opt.fps||40,onStep:opt.onStep||function(){},onComplete:opt.onComplete||function(){}};}Effect.prototype={//define how the animation startsstart:function(){//return if we're currently performing an animationif(this.timer)return;//trigger the animationvarthat=this,fps=this.opt.fps;this.time=+newDate;this.timer=setInterval(function(){that.step();},Math.round(1000/fps));},//triggered at each interval stepstep:function(){varcurrentTime=+newDate,time=this.time,opt=this.opt;//check if the time interval already exceeds the duration if(currentTime<time+opt.duration){//if not, calculate our animation progressvardelta=(currentTime-time)/opt.duration;opt.onStep(delta);}else{//we already exceeded the duration, stop the effect//and call the onComplete callbackthis.timer=clearInterval(this.timer);opt.onStep(1);opt.onComplete();}}};
One very common operation to do with delta is to change the interval [0, 1] of delta to our desired from and to values that we want to compute for our element. A clever thing to do would be to declare this method as a class method for Effect. We'll call it compute:
The animation code defined above could be extended in different ways. For example, this class could be slightly modified to accept a DOM element in its constructor and modify style properties of that element when performing an animation. The code could look like this:
//define the class constructorfunctionEffect(opt){this.opt={element:opt.element,duration:opt.duration||1000,fps:opt.fps||40,onComplete:opt.onComplete||function(){}};}Effect.prototype={//props contains a hash with style propertiesstart:function(props){if(this.timer)return;varthat=this,fps=this.opt.fps;this.time=+newDate;this.timer=setInterval(function(){that.step(props);},Math.round(1000/fps));},//triggered at each interval stepstep:function(props){varcurrentTime=+newDate,time=this.time,opt=this.opt;if(currentTime<time+opt.duration){vardelta=(currentTime-time)/opt.duration;//set the element style propertiesthis.setProps(opt.element,props,delta);}else{this.timer=clearInterval(this.timer);this.setProps(opt.element,props,1);opt.onComplete();}},//set style properties. Properties must be//in camelcase format.setProps:function(elem,props,delta){varstyle=elem.style;for(varpropinprops){varvalues=props[prop];style[prop]=Effect.compute(values[0],values[1],delta)+(values[2]||'');}}};
Other extensions might involve normalizing style keywords, adding effect transitions, adding pauseresume methods, and/or using more OO JS idioms when coding these classes.
I hope you got to know a little bit more about animation internals and please if you have any advice on this code, which as I told before is just for demonstration, I'll be pleased to hear you!