sans-serif   serif

«

jQuery Conference Slides and Demo

April 24, 2010

Just spoke at jQuery conference, the largest javascript conference to date (as stated by Resig.) It really was a great turnout and I’ve met tons of cool people.

I only had a half an hour, which flew by and is hardly enough time to talk about MooTools Class AND the jQuery mutator. Had several people tell me I convinced them to give MooTools a shot with their apps since they can keep jQuery for the DOM. That’s always good news.

Update: moo4q.com

There’s now a site dedicated to this technique: http://moo4q.com, check it out.

Slides and Demo

And now for your viewing pleasure:

Source of the demo

Mutator

Gist

Class.Mutators.jQuery = function(name){
    var self = this;
    jQuery.fn[name] = function(arg){
        var instance = this.data(name);
        if ($type(arg) == 'string'){
            var prop = instance[arg];
            if ($type(prop) == 'function'){
                var returns = prop.apply(instance, Array.slice(arguments, 1));
                return (returns == instance) ? this : returns;
            } else if (arguments.length == 1){
                return prop;
            }
            instance[arg] = arguments[1];
        } else {
            if (instance) return instance;
            this.data(name, new self(this.selector, arg));
        }
        return this;
    };
};

Human

var Human = new Class({

    Implements: [Options, Events, Loop],

        options: {
            rest: {
                frames: 14,
                fileName: 'images/rest/rest_',
                speed: 100
            }
        },

    jQuery: 'human',

    energy: 5,
    isAlive: true,
    animatingOnce: false,

    initialize: function(selector, options){
        this.setOptions(options);
        this.image = $(selector);
        this.name = this.image.attr('name') || 'unkown';
        this.currentAnimation = this.options.rest;
        this.loadImages(this.options.rest);
        this.setLoop(this.animate, this.currentAnimation.speed).startLoop();
    },

    eat: function(){
        if(this.isAlive){
            this.energy++;
            this.fireEvent('eat');
        }
    },

    die: function(killer){
        if(this.isAlive){
            this.energy = 0;
            this.isAlive = false;
            killer.kill(this);
            this.fireEvent('die', killer);
        }
        return this;
    },

    revive: function(){
        if(!this.isAlive){
            this.energy = 1;
            this.isAlive = true;
            this.fireEvent('revive');
        }
        return this;
    },

    animate: function(){
        if(this.loopCount > this.currentAnimation.frames) this.loopCount = 1;
        this.image.attr('src', this.currentAnimation.fileName + this.loopCount.zeroPad(4) + '.png');
        return this;
    },

    changeAnimation: function(animation){
        if(!this.animatingOnce){
            this.currentAnimation = animation;
            this.resetLoop().setLoop(this.animate, animation.speed);    
        }
        return this;
    },

    animateOnce: function(animation, stop){
        if(!this.animatingOnce){
            var original = this.currentAnimation;
            this.changeAnimation(animation);
            this.animatingOnce = true;
            var delay = this.currentAnimation.frames * this.currentAnimation.speed;
            var self = this;
            (function(){
                self.animatingOnce = false;
                (stop) ? self.stopLoop() : self.changeAnimation(original);
            }).delay(delay);
        }
        return this;
    },

    loadImages: function(animation){
        (animation.frames - 1).times(function(num){
            $('<img/>', { src: animation.fileName + (num + 1).zeroPad(4) + '.png'});
        }, this);
        return this;
    }

});

Number.implement({
    zeroPad: function(length){
        var str = '' + this;
        while (str.length < length) str = '0' + str;
        return str;
    }
});

Warrior

var Warrior = new Class({

    energy: 1000,
    kills: 0,
    attack: function(target){
        if(!this.animatingOnce) {
            this.fireEvent('attack', target);
            this.animateOnce(this.options.attack);
            if(target.isAlive){
                var delay = this.options.attack.frames / 2.3 * this.options.attack.speed;
                target.getAttacked.delay(delay, target, this);
                this.fireEvent.delay(delay, this, ['attackComplete', target]);
            }
        }
        return this;
    },

    kill: function(victim){
        this.kills++;
        this.fireEvent('kill', victim);
        return this;
    }

});

Ninja

var Ninja = new Class({

    Extends: Human,
    Implements: Warrior,

        options: {
            side: 'evil',
            attack: {
                frames: 44,
                fileName: 'images/attack/attack_',
                speed: 40
            },
            rest:{
                frames: 14,
                fileName: 'images/ninja-rest/rest_',
                speed: 75 
            }
        },

    jQuery: 'ninja',

    initialize: function(selector, options){
        this.parent(selector, options);
        this.loadImages(this.options.attack);
        this.side = this.options.side;
    }

});

Victim

var Victim = new Class({

    getAttacked: function(attacker){
        this.stopLoop();
        this.image.attr('src', this.options.attackedSrc);
        this.startLoop.delay(100, this);
        this.energy--;
        this.fireEvent('getAttacked', attacker);
        if(this.energy == 0) this.die(attacker);
    },

    die: function(killer){
        if(this.isAlive){
            this.parent(killer);
            this.animateOnce(this.options.die, true);
        }
        return this;
    },

    revive: function(){
        if(!this.isAlive){
            this.parent();
            this.changeAnimation(this.options.rest).startLoop();
        }
        return this;
    }

});

Civilian

var Civilian = new Class({

    Extends: Human,
    Implements: Victim,

        options: {
            die: {
                frames: 29,
                fileName: 'images/die/die_',
                speed: 75
            },
            attackedSrc: 'images/die/die_0000.png'
        },

    jQuery: 'civilian',

    initialize: function(selector, options){
        this.parent(selector, options);
        this.loadImages(this.options.die);
    }

});

Domready

$(document).ready(function(){

    // create our humans
    $('#bob').civilian(); // creates instance
    $('#ryu').ninja({side: 'evil'}); // pass in some options

    // keyboard events
    $(document).bind('keydown',function(event){

        if(event.keyCode == 65)
            $('#ryu').ninja('attack', $('#bob').civilian()); 
            // $('#bob').civilian() a second time returns the instance
            // just like `var bob = new Civilian('#bob')`, it returns a object like `bob`
            // so `$('#bob').civilian().die() ~= $('#bob').civilian('die')`
            // except the first returns the object and the second returns the jquery object

        if(event.keyCode == 82) $('#bob').civilian('revive');

        if(event.keyCode == 69) $('#bob').civilian('eat');

    });

    // log function
    var setLog = function(message, className){
        className = className || 'message';
        var top = $('#log')[0].scrollHeight;
        $('#log').append('<p class=' + className + '>' + message + '</p>').stop().animate({scrollTop: top}, 1000);
    }

    // instance events
    // when things happen we can do stuff, like the callbacks you're used to
    // Here we get the instance of civilian by calling the civilian on bob again
    // and add the events to it, but you can also ...
    $('#bob').civilian().addEvents({
        onDie: function(killer){
            // events can have arguments, killer is the instance of $('#ryu').ninja()
            // and `this` is the instance of $('#bob').civilian() in here.
            setLog(this.name + ' dies by the hand of ' + killer.name, 'die'); 
        },
        onRevive: function(){ 
            setLog(this.name + ' is Revived', 'revive'); 
        },
        onGetAttacked: function(attacker){ 
            setLog(this.name + ' is hit! He has ' + this.energy + ' energy left after ' + attacker.name + "'s attack!", 'getAttacked'); 
        },
        onEat: function(){
            setLog(this.name + ' ate some food. His energy is now ' + this.energy, 'eat');
        }
    });

    // ... just use 'addEvents' as an argument to the class to add the events
    $('#ryu').ninja('addEvents',{
        onAttack: function(target){ 
            setLog(this.name + ' attacks ' + target.name + '!', 'attack'); 
        },
        onKill: function(target){
            var plural = (this.kills == 1) ? 'kill' : 'kills';
            setLog(this.name + ' killed ' + target.name + '!! Ryu now has ' + this.kills + ' ' + plural, 'kill');
            setLog.delay(3000, this, ["You find an <span>Emporer's Hairpin</span> on " + target.name, 'find']);
            setLog.delay(3000, this, ["You find a <span>handful of paint brushes</span> on " + target.name, 'find']);
            setLog.delay(3000, this, ["You find <span>4 " + target.name + " skins</span> on " + target.name, 'find']);
        }
    });

});

Related Posts:

  • No Related Posts

Comments

  • http://vombat.tumblr.com Anurag

    Holy crap, awesome job with the demo :)

    Really like the slides too, but it felt a little short, but I can’t imagine packing even that much quality information in a 30 min session.

  • http://diegotres.com Diego Tres

    I’ve used at initialize something like:

    initialize: function( jquery_element ) { $.extend( this , jquery_element ); }

    and to instance this:

    var ninja = new Ninja( $(‘#ninja’) );

    What do you think about?

  • Jeff

    Pretty spiffy. I’m just learning MooTools & JQuery and found your articles. After years of creating my own libraries, I’ve decided I need to start working w/ other people ideas instead of off on my own tangents of exploration. :-)

    One odd thing about the demo is have you noticed in IE 7 & IE 8 it constantly loads the images from your server instead of caching them. In Safari 4.0.5 it runs perfect caching the images. I’m not sure what in the code IE is having issues with, but it definitely takes a hit on performance & traffic compared to Safari.

  • Ryan Florence

    @Jeff – This demo is certainly not expected to run in IE, considering IE in all of it’s awful flavors makes up less than 10% of my visitors. I’ll be writing a new post with details and a cross browser demo of how to use this technique.

    I knew from the start that guys like Valerio Proietti and Jon Resig know more than me!

    @Diego – Not sure what you’re trying to do there. My API allows you to use either:

        // normal javascript
        var ninja = new Ninja('#ninja')
        // jQuery API
        $('#ninja').ninja();
    
  • Jeff

    I know IE definitely can leave a bad taste in one’s mouth for a long time. Your website stats may change though, if IE is pulling the images constantly from your host. :-) I’m betting if you’re tracking your bandwidth traffic you will be able to tell when an IE person was looking at the demo real quick! That spike in the graph will make you laugh a good one!

    Enjoy!

  • Ryan Florence

    @Jeff – I guess before anybody accuses me of whatever, blah blah blah … that demo was used during a talk presented on my own machine that I opened up to the public to check out.

    I think after reading some posts like this one, it’s clear I care about browser support for my clients. In fact, I demand it.

    I’m also on shared hosting here with unlimited bandwidth, so whatever …

    And finally, yes, you’re right … my browser stats with regard to IE will change soon. This site is going all HTML5, CSS3 with complete disregard for anything that doesn’t support the commonly supported features. Which means IE visitors won’t have a very good experience at all, if I even offer one!

  • http://zudolab.net/blog/?p=228 zudolog » jQuery conference 2010: San Francisco Bay Area に関するスライドら

    [...] Oriented jQuery with MooTools (Pigs take flight)” http://ryanflorence.com/jquery-conference-slides-and-demo/ [...]

blog comments powered by Disqus

Comments RSS