0

I'm having trouble with a Mootools Fx.Morph object. I expected I'd be able to use a single object several times to create different effects. I'd like to toggle the effect on a single div. But my effect only fires the first time. Are there any Fx.Morph wizards out there who could advise?

This is my class

    var F = new Class({

    Implements: [Options, Events],

    myFX: null,

    dDiv: null,

    options: {
        container: null,
        width: '250px',
        background: '#ccc'
    },

    initialize: function(options) {
        this.setOptions(options);
        this.addDemoDiv();
    },

    addDemoDiv: function() {
        var dDiv = new Element('div', {
            'class': 'myClass',
            html: 'Click me!',
            styles: {
                padding: '20px',
                border: '1px solid #999',
                width: this.options.width,
                background: this.options.background
            },
            events: {
                click:  this.animate.bind(this)
            }
        });

        if (!this.container) {
            this.dDiv = dDiv.inject(document.body);
        } else {
            this.dDiv = dDiv.inject(this.container);
        }

        this.myFx = new Fx.Morph(dDiv);
    },

    animate: function(e) {
        this.dDiv.set('html', 'Hello world');
        this.myFx.start({
            height: 200,
            width: 500,
            link: 'cancel',
            onComplete: function(){
                this.dDiv.removeEvent('click', this.animate);
                this.dDiv.addEvent('click', this.deanimate.bind(this));
            }.bind(this)
        });
    },

    deanimate: function(e) {
        this.dDiv.set('html', 'Click me!');
        this.myFx.start({
            height: 20,
            width: 250,
            link: 'cancel',
            onComplete: function() {
                this.dDiv.removeEvent('click', this.deanimate);
                this.dDiv.addEvent('click', this.animate.bind(this));       
            }.bind(this)
        });
    }

});

window.addEvent('domready', function() {

    var item = new F({cntainer: 'container', background: '#ddd', width: '250px'});

});

I've tried quite a few things, including giving up on the Fx.Morph property and just calling .morph on the div with each click. This seemed to cause horrendous browser problems, maybe because it was creating a new Morph oject with each click and leaking memory.

The deanimate() method is definitely being called, because this.dDiv.set('html', 'Click me!'); is working. But the div won't shrink back. I've tried applying other effects, like background colour, but they're ignored too.

Does this mean an Fx.Morph object is destroyed after you run start()?

The onComplete handler of Fx.Morph also doesn't behave as you would expect. When I put a console.log in the onComplete of the animate() method, the log message gets displayed long before the animation's finished. So is onComplete just when the parsing of the script is complete?

Or is this just me being a chump? Any pointers gratefully received!

======================

Added later

I've been able to get around this problem by losing the Fx.Morph object and animating with .morph and a toggle variable, like this

    animate: function(e) {
        if (!this.anim) {
            this.dDiv.set('html', 'Hello world');
            this.dDiv.morph({
                background: '#666',
                width: 500,
                height: 250
            });
            this.anim = true;
        } else {
            this.dDiv.set('html', 'Click me!');
            this.dDiv.morph({
                background: '#eee',
                width: 250,
                height: 100
            });
            this.anim = false;
        }
    }

But if anybody can shed any light on why the first version wasn't working I'd be very grateful!

0

1 Answer 1

1

this is an error in binding and removeEvent.

this.animate.bind(this) returns a function that is NOT the same as this.animate which you compare against when you removeEvent.

if you bind event callbacks that you need to remove, you should store them: http://jsfiddle.net/yLhHG/ (incomplete but it does first shrink. read more:

basically:

this.boundEvents = {
    click: this.animate.bind(this)
};

...

this.dDiv.removeEvent('click', this.boundEvents.click);

or:

this.dDiv.addEvent('click', this.boundEvent = this.animate.bind(this));

...

this.dDiv.removeEvent("click", this.boundEvent);

and finally:

you can totally rewrite this to have a single method but pass a different object. will update it in a moment.

there: http://jsfiddle.net/yLhHG/1/

var F = new Class({

    Implements: [Options, Events],

    myFX: null,

    dDiv: null,

    options: {
        container: null,
        width: '250px',
        background: '#ccc',
        fx: {
            animate: {
                props: {
                    height: 200,
                    width: 500
                },
                html: "hello world"
            },
            deanimate: {
                props: {
                    height: 20,
                    width: 250
                },
                html: "bye world"
            }
        }
    },

    initialize: function(options) {
        this.setOptions(options);
        this.container = this.options.container || document.body;
        this.addDemoDiv();
    },

    addDemoDiv: function() {
        this.element =  new Element('div', {
            'class': 'myClass',
            html: 'Click me!',
            styles: {
                padding: '20px',
                border: '1px solid #999',
                width: this.options.width,
                background: this.options.background
            },
            events: {
                click: this.animate.bind(this)
            },
            morph: {
                link: "cancel",
                onComplete: function() {

                }.bind(this),
                onStart: function() {
                    this.element.set("html", this.options.fx[this.action].html);
                }.bind(this),
                onCancel: function() {
                   //
                }
            }
        }).inject(this.container);

    },

    animate: function(e) {
        this.action = this.action == "animate" ? "deanimate" : "animate";
        this.element.morph(this.options.fx[this.action].props);
    }
});

new F({
    cntainer: 'container',
    background: '#ddd',
    width: '250px'
});
3

Not the answer you're looking for? Browse other questions tagged or ask your own question.