Yesterday I was helping a guy with MooTools 1.2. He wanted to chain some effects on an element and, when all effects completed, to perform a simple fade in on another element.
Basically, he had a box element and an image inside that box. He wanted first to make the box growing horizontally, then vertically and finally to fade the image in. It
sounds pretty simple, and in fact it is. There are a couple of approaches to achieve it. I will not show you how new Fx work, for that you can have a nice reading at Tom’s great article. I will instead show you some of the approaches and introduce
onChainComplete.
Startup
We first instantiate our Fx.Morph, we’re going to refer to it in all the following approaches. Here we go:
// We cache image and box elements, since we're going // to use them later. var img = $('myImg'), box = $('myBox'); // We now instantiate the effect, setting 1 second as duration, Quad.easeOut as transition // and, important, we set chain as effects link. var fx = new Fx.Morph(box, {duration: 1000, transition: 'quad:out', link: 'chain'});
First approach
In the first approach I’m gonna show you the same way, and the only one, as for MooTools 1.11. We’re going to “manually” chain the start method by the Chain Class.
// First step: horizontal transition fx.start({ 'left': 30, 'width': 150 }).chain(function() { // Second step: vertical transition // We do something totally unrelated to effects console.log('Hello world!'); // Now we morph our box vertically this.start({ 'height': 100, 'width': 100 }); }).chain(function() { // Third and last step: Now we can fade in the image img.fade('in'); });
This certainly looks really horrible as code, it definitely doesn’t reflect MooTools elegance at all I’d say, and in particular it doesn’t reflect MooTools 1.2. But it works, and it explains very well how Chain work and how to use it.
The chain is a stack where functions are pushed in. When one function finish its execution, it get shifted and the next one function get called. Noticed that useless
console.log? That’s to demostrate that you can do whatever you want in the chained function.
With this approach link: 'chain' is absolutely unneeded. Even if we’re using chain as option in our Fx istance, we aren’t using what link: 'chain'
provides.
Second approach
What MooTools 1.2 introduced is link: 'chain'. This great option allow us to make effects chainable in a really elegant and efficient way. Taking as example the First approach, I’m going to reproduce it taking advantage of link: 'chain'.
// First step: horizontal transition fx.start({ 'left': 30, 'width': 150 }).start({ // Second step: vertical transition 'height': 100, 'width': 100 }).chain(function() { // Third and last step: Now we can fade in the image img.fade('in'); });
As you can see, the second start is no more a .chain(function() {}), that’s exactly what link: 'chain' does. It allows to chain effects and nothing more. The
difference from first approach is that we cannot do anything but effects chaining. Thinking about it, it makes sense, what we wanted to provide was exactly
that: effects chaining. So we can now do pretty cool stuff like:
var example = $('anImage').set('tween', {link: 'chain'}); example.fade('in').fade('out').fade('toggle').tween('height', 50).fade('out');
Getting back to the second approach, the question is, how do we eliminate the final chaining to fade the image in? Simply we can’t in an elegant way. The best solution adoptable is a bit
tricky. Analyzing the case, we can see that what we effectively need is to have a onComplete event that fires when our chain stack is emptied of all the chained effects.
Something like:
// We add the onComplete event that fades in the image // This becomes our Third and last step. fx.addEvent('onComplete', function() { img.fade('in'); }); // First step: horizontal transition fx.start({ 'left': 30, 'width': 150 }).start({ // Second step: vertical transition 'height': 100, 'width': 100 });
This doesn’t work as expected! In fact, the onComplete will fires at the end of every transition. If we had 100 transitions, onComplete
had fired 100 times. So, to help that guy, I provided to him an hacky way to get rid of it, by modifying the onComplete as below:
fx.addEvent('onComplete', function() { if (!this.$chain.length) img.fade('in'); });
This had inspired me and I had a talk with Valerio, he agreed with me and we were feeling like something was missing. We then came out with a quick fix: onChainComplete event.
Conclusion: onChainComplete
The new onChainComplete event works the same as onComplete we all know, except it fires only when all the chain stack is empty. i.e.
fx.addEvents({ 'onComplete': function() { console.log('single effect completed!'); }, 'onChainComplete': function() { console.log('all effects have completed!'); } });
Q: Can I use both onComplete onChainComplete?
A: Of course you can. OnComplete fires at the end of each effect completion, onChainComplete at the last one.
Q: Where onChainComplete comes handy?
A: It’s useful only when using link: 'chain'
Final result
To complete, here the final result:
// Third and last step: When all effects have completed // we want the image fades in. fx.addEvent('onChainComplete', function() { img.fade('in'); }); // First step: horizontal transition fx.start({ 'left': 30, 'width': 150 }).start({ // Second step: vertical transition 'height': 100, 'width': 100 });
Thank you for this tutorial, it’s very well explained :-)