Question

I made a somewhat simple Mootools animation that makes an element move on a circular path using two separate Fx.Morph variables.. My problem is that I need to be able to cancel the animation, and I can't for the life of me figure out how to handle cancelling both morphs without directly calling them or saving them as a global variable.

Here's a simplified version of the code I'm using : http://jsfiddle.net/4vBj9/2/

I've tried using $(img).get('morph').cancel(); but that doesn't seem to work on elements with multiple Fx.

EDIT: Occurs to me that I forgot to mention that the event which cancels the animations may not happen in the same scope as where it's instantiated, so I can't really call the variables handling the morphs.

Was it helpful?

Solution

erm. you can use local element storage. by default, the .get('morph') will retrieve the first fx instance for you but since you have multiple / make a new one every time and don't use the .get/.set, you are better off doing something like this instead:

;(function(){
    var img = document.id('img');

    $('add').addEvent('click', function() {
        // try to get from storage, set empty array as default
        var fxs = img.retrieve('fxs', []);
        // if empty, make instances, store into array
        if (!fxs.length) {
                [].push.apply(fxs, [
                new Fx.Morph(img, {
                    duration: 1500,
                    transition: Fx.Transitions.linear.easeOut
                }),
                new Fx.Morph(img, {
                    duration: 750,
                    transition: Fx.Transitions.easeOutCirc
                })
            ]);
            // save it as one off
            img.store('fxs', fxs);
        }
        // reference as array elements
        fxs[0].start({
            top: 175
        });
        fxs[1].start({
            left: 175
        }).chain(function() {
            // this === fx instance. DRY
            this.options.transition = Fx.Transitions.easeInCirc;
            this.options.duration = 764;
            this.start({
                left: 10
            });
        });
    });

    $('rem').addEvent('click', function() {
       img.removeProperty('style');
    });

}());

much later, completely independent code:

;(function(){ // new scope
    $('box').addEvent('click', function(){
        var fxs = $('img').retrieve('fxs');
        fxs && fxs.invoke('stop');
    });
}());

this achieves decoupling of code and reliance on variables and makes it DRY and encapsulated--it also re-uses the same fx instances so it's gonna be faster to run after first start.

here's your thing. http://jsfiddle.net/4vBj9/6/

if you're doing complicated and nested animations that need to be smooth in javascript, consider https://github.com/julianshapiro/velocity - it's BY FAR the fastest way to animate anything, including CSS transitions (check the benchmarks). oh - also can do css3 props, which mootools can't.

OTHER TIPS

Since you are trying to create the animation based on curve, I'll show you more elegant way to achieve that. It is based on extending Fx class and adding the support for Bezier curves.

Bezier curves

Bezier curves are used in computer graphics to calculate smooth curves and to describe paths. An advantage here is that only one instance of FX is needed, which will improve performance, and also, you can create an animation that is based on any type of curve.

Base Fx Class

The first part is to extend base Fx class ( the base class for all Fx instances ) and thus to get the full control over the animation. We can use now the set() function, fired on every step of the animation, to manually calculate and set the values needed for the effect. The most important part here is the get_points() function, where is calculated the current position for the animated element. The formula used is for a cubic curve.

    Fx.Bezier = new Class({

        Extends: Fx,

        initialize: function ( element, options ) {
            this.element = document.id(element);
            this.parent(options);
        },

        set: function ( now ) {
            var pos = this.get_points( now );
            this.element.setStyles( { 'left': pos.x, 'top': pos.y } );
            return this;
        },  

        start: function( from, to ) {
            return this.parent( from, to );
        },

        get_points: function( t ) {
            var p = this.options.points;
            var t1 = 1-t;
            return {
                x: p.x0*t*t*t + p.x1*3*t*t*t1 + p.x2*3*t*t1*t1 + p.x3*t1*t1*t1,
                y: p.y0*t*t*t + p.y1*3*t*t*t1 + p.y2*3*t*t1*t1 + p.y3*t1*t1*t1
            };
        }

    }); 

Element.Properties

To be able to set the Fx.Bezier as a dynamic argument in the same way as tween or morph, we have to create the element property in Element.Properties Object.

    Element.Properties.bezier = {

        set: function( options ){
            this.get( 'bezier' ).cancel().setOptions( options );
            return this;
        },

        get: function(){
            var bezier = this.retrieve( 'bezier' );
            if ( !bezier ){
                bezier = new Fx.Bezier( this, {link: 'cancel'} );
                this.store( 'bezier', bezier );
            }
            return bezier;
        }

    };

Usage

Now we can use custom FX as either, stored in a variable, or stored in the element storage. Here is an example of how to store in a variable, also check out the fiddle where is an example how to use element storage.

var myBezier = new Fx.Bezier( 'img' , { 
    points: { x0: 10, y0: 10, x1: 250, y1: 0, x2: 250, y2: 250, x3: 100, y3: 250 },
    duration: 1500, 
    transition: Fx.Transitions.Expo.easeIn
});

myBezier
    .start( 1, 0 )
    .chain( function() {
        var points = this.options.points;

        this.setOptions({
            points: { x0: points.x3, y0: points.y3, x1: (points.x3-points.x2)+points.x3, y1: points.y2, x2: 0, y2: 0, x3: 150, y3: 150 },
            transition: Fx.Transitions.Expo.easeOut
        }).start( 1, 0 );
    });

Fiddle: http://jsfiddle.net/m5ErG/

A few links on the subject:

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top