Mootools Chaining for Image Effects
May 18th, 2009 | Published in Chaining, Demo, Image Effects, Javascript, Mootools
The internet abounds with JavaScript code samples that produce various fade effects on images. The multitude (some might say excess) of gallery slideshows attest to the popularity of this type of application. A huge help has been the development of JavaScript frameworks like Mootools and jQuery, which make it incredibly easy to produce simple image effects and even chain them together to achieve more complex results.
One thing you don’t see a lot of, though, are image gallery applications where the effects are triggered to begin at arbitrary time intervals. For example, say you have a large group of images to which you’re going to apply individual fade-in effects. Not hard to do, but what if you want to have the fade-in for the next image begin before the effect on the preceeding image (or several preceeding images) has completed? That’s the question I asked myself: for me the answer was MooGal, a small class I wrote with the help of Mootools.
Instead of sitting there scratching your head and wondering exactly what I’m talking about, please click Here to see a demo of the effect in action.
Let’s take a look at some of the code, shall we?
var MooGal = new Class({
Implements: [Chain, Options],
options: {
duration: 3000,
delay: 400,
transition: Fx.Transitions.Sine.easeInOut
},
initialize: function(gallery, options){
this.gallery = $$(gallery).length == 0 ? $$('.' + gallery) : $$(gallery);
if (!this.gallery.length) return;
this.setOptions(options);
this.timerArray = [];
this.gallerySort();
this.galleryFx();
this.load();
},
By implementing the Chain class we put its methods and properties at the disposal of MooGal. In the demo, the initialize method takes a CSS class selector (”.moogal”) and uses the default options to set the duration, delay and transition effect. The moogal class consists of a number of unordered list elements, each wrapping an image element. Because each of these elements will have its own Fx.Tween instance and basically be independent of one another, this.timerArray is set up to hold the timer ids associated with the function delays that control the overall timing.
The next two key methods are galleryFx() and load():
/**
Creates a tween instance for each element and stores it in the
element.
*/
galleryFx: function(){
this.gallery.each(function(picture){
picture.store('fx', new Fx.Tween(picture,
{property:'opacity', duration:this.options.duration,
link:'cancel', transition:this.options.transition}));
}, this);
return this.gallery;
},
/**
Retrieves tween instances and places them on the call stack.
*/
load: function(){
this.clearChain(); // clear the call stack of any remaining tween instances.
this.gallery.each(function(picture){
this.chain(
function() {picture.retrieve('fx').set(0).start(1);}
);
}, this);
},
The load() method is where we want to focus our attention. It takes the Fx.Tween instances that were created in galleryFx() and uses the chain() method to place them on our class’ call stack. What this really does is load these function definitions into an internal Mootools array called $chain. One of the interesting things about JavaScript is that you can treat functions like data and load them into arrays, assign references to them in variables, etc. But the important thing to note is that this.chain simply gets our functions ready: it does not invoke them. That comes later with the run() method:
run: function() {
var i = 0;
// $chain is an internal Mootools array that holds the chained functions.
var chainLength = this.$chain.length;
while (i < chainLength) {
// set up delays and start running the transitions one after another.
// binding is tricky here.
// with a default delay of 400ms, the first effect starts at t=0, the second at t=0.4sec,
// the third at t=0.8sec, the fourth at t=1.2sec, etc.
this.timer = (function() {this.callChain();}.bind(this)).delay(i * this.options.delay);
this.timerArray[i] = this.timer; // store timer ids in a timer array.
i++;
}
return this;
}
The run() method takes whatever Fx.Tween instances are remaining on the call stack and employs the callChain method to execute them. The most critical part of the entire class, however, is the use of the delay method, which essentially staggers their execution out in time. The reason for using the $chain array and storing the timer ids in this.timerArray is due to the cancel() method:
cancel: function(cancelled){
if (cancelled){
this.clearChain();
this.gallery.each(function(picture){
picture.retrieve('fx').cancel().set(0);
});
}
// clear the timers to prevent any more transitions from starting.
for (i = 0; i < this.timerArray.length; i++){
$clear(this.timerArray[i]);
}
this.timerArray.empty(); // empty the timer id array.
},
In other words, we need to be able to clear all the timers so that any remaining delayed functions are prevented from happening if the effects are cancelled or paused. The $chain array holds the “unused” Fx.Tween instances we’ll need if the action is resumed, as is possible in the demo.
To make the timing more apparent, you can lengthen the duration option in relation to the delay. If you’d like to download the MooGal code and play around with it, please get it here.