2011/07/15

Plugin jQuery Sliding Buttons

Después de algún tiempo de diseño cociendo algunas cosas, he salido de la cueva y os presento otro plugin de jQuery totalmente hecho por mí. Ante vosotros está el magnífico Sliding Buttons!

          

Este plugin transforma un inocente span o div con dos contenedores de tipo inline dentro en un vistoso botón cuyos contenidos (primer contendor) aparece y desaparece al pasar el ratón sobre él. Soporta eventos y funciona!

Ahí va el jsFiddle de rigor y como siempre, si encuentras algún bug o lo mejoras, decídmelo, y si lo usas, cuéntamelo también, que al menos no tenga sensación de haber perdido el tiempo.


Ejemplo

Código javascript
/**
 * jquery.slideButton-0.1.js - Slide Button plugin for jQuery
 * ==========================================================
 *  (C) 2011 José Ramón Díaz - jrdiazweb@gmail.com
 *
 * http://3nibbles.blogspot.com/2011/07/plugin-jquery-sliding-buttons.html
 *
 * INSTANTIATION
 * Any container with the .slideButton class will be initialized
 * on domReady event using the options defined in object
 * "slideButtonDefaults". This feature can be disabled by setting
 * the variable "slideButtonAutoload" to the false value.
 *
 * Manual instantiation
 *     $('.slideButton').slideButton( [ options_object ] );
 *
 * OPTIONS
 *     - direction    : 'left',   Slide direction "left" = LtR, "right" = RtL
 *     - border       : '3px',    Border width == difference in height of control(>) and contents(<)
 *     - radius       : '20px',   Border radius of the slideButton
 *     - height       : '36px',   Maximum height of the slideButton. Includes all borders
 *     - width        : '70px',   Minimum width. Applied to control
 *     - deployedWidth: '225px',  Maximum width. Applied to container and deployed contents
 *     - inAnimation  : 'swing',  Function used to deploy (slide in) the elements. For more functions needs the easing packs
 *     - outAnimation : 'swing',  Function used to hide (slide out) the elements. For more functions needs the easing packs
 *     - inSpeed      : 500,      deploy effect duration
 *     - outSpeed     : 300,      hide effect duration
 *     - onClick      : null,     button click trigger callback function
 *     - onBlur       : null,     blur (slide out) trigger callback function
 *     - onDeploy     : null,     deploy (slide in) trigger callback function
 *     - onHide       : null,     hide (end of slide out) trigger callback function
 *
 * API
 *     - show()    Deploys the sliding contents
 *     - hide()    Hides the sliding contents
 *     - lock()    Locks the sliding contents open
 *     - locked    Indicates that the control is locked
 *     - deployed  Indicates that the control is deployed
 *
 * CLASSES
 *     - Container: .slideButton, .deployed, .clicked
 *     - Button:    .button
 *     - Content:   .content
 *
 * HTML format
 *     *
 * Valid HTML content and control containers are any inline element (a, span, etc...).
 * Container must be div or span.
 *
 * Legal stuff
 *     You are free to use this code, but you must give credit and/or keep header intact.
 *     Please, tell me if you find it useful. An email will be enough.
 *     If you enhance this code or correct a bug, please, tell me.
 */
var slideButtonDefaults = {};   // The default options used for all auto loading sliding buttons
var slideButtonAutoload = true; // Enables the autoinstantiation feature for .slidingButton

(function( $ ) {
    // Private members
    function SlideButton( el, options ) {
        this.container = $(el);
        this.cA = null;
        this.cB = null;

        //this.el.attr('locked', 'off');

        // States control
        this.locked   = false;
        this.deployed = false;

        // Default options
        this.options = {
                direction    : 'right',   // Slide direction "left" = LtR, "right" = RtL
                border       : '3px',    // Border width == difference in height of control(>) and contents(<)
                radius       : '20px',   // Border radius of the slideButton
                height       : '36px',   // Maximum height of the slideButton. Includes all borders
                width        : '70px',   // Minimum width. Applied to control
                deployedWidth: '225px',  // Maximum width. Applied to container and deployed contents

                inAnimation  : 'swing',  // Function used to deploy (slide in) the elements. For more functions needs the easing packs
                outAnimation : 'swing',  // Function used to hide (slide out) the elements. For more functions needs the easing packs
                inSpeed      : 300,      // deploy effect duration
                outSpeed     : 200,      // hide effect duration

                onClick      : null,     // button click trigger callback function
                onBlur       : null,     // blur (slide out) trigger callback function
                onDeploy     : null,     // deploy (slide in) trigger callback function
                onHide       : null,     // hide (end of slide out) trigger callback function
        };

        slideButtonDefaults = this.options; // Sets the initial value of global default options

        this.setOptions( options );
        this.initialize();
    }

    // Instantiation
    $.fn.slideButton = function( options ) {
        return new SlideButton(this.get(0) || $('<span />') || $('<div />'), options);
    };

    // Public Members
    SlideButton.prototype = {
        // Plugin functions
        killerFn: null,

        initialize: function() {
            var me, o, c, children, cA, cB, h1, h2, w1, w2, w3, r1, r2, b;
            me = this;
            o  = me.options;
            c  = me.container;

            // Sets objects and variables initial values
            children = c.children();
            cA = me.cA = $( children[0] );
            cB = me.cB = $( children[1] );

            // Widths and heights
            b  =   parseInt(o.border);                  // Border
            h1 =   parseInt( o.height ) + 'px';         // Container/Contents height
            h2 = ( parseInt( h1 ) - (b * 2) ) + 'px'; // Control height
            w1 =   parseInt( o.deployedWidth ) + 'px';  // Container and deployed width
            w2 =   parseInt( o.width ) + 'px';          // Contents width
            w3 = ( parseInt( w2 ) - (b * 2) ) + 'px';  // Control width
            r1 =   parseInt( o.radius ) + 'px';         // External border radius
            r2 = ( parseInt( o.radius ) - b ) + 'px'; // Internal border radius

            // Container modifications
            if( !c.hasClass('slideButton') ) me.container.addClass( 'slideButton' );
            c.width( w1 );
            c.height( h1 );
            //me.container.height(o.height);
            c.css({ 'position': 'relative', 'overflow': 'hidden' });

            // Chidren modifications
            cA.addClass( 'content' ); //o.direction + 'A content' );
            cB.addClass( 'button'  ); //o.direction + 'B button' );

            // Wraps sliding contents into a span
            var text = cA.get(0).innerHTML;
            cA.get(0).innerHTML = '' + text + '';
            cA.find('span:first-child').hide();

            // Sets styles
            c.data( 'initialWidth', w1 ); // Stores the initial width in container data
            cA.width( w2 );
            cB.width( w3 );
            cA.height( h1 );
            cB.height( h2 );
            cA.css({ 'position': 'absolute', 'top': '0px' , 'lineHeight': h1, 'zIndex': 0, 'borderRadius': r1 });
            cB.css({ 'position': 'absolute', 'top': b+'px', 'lineHeight': h2, 'zIndex': 1, 'borderRadius': r2 });

            if(o.direction == 'left')
            {
                cA.css({ 'right': '0px' , 'textAlign': 'left' });
                cB.css({ 'right': b+'px' });
            }
            else
            {
                cA.css({ 'left': '0px' , 'textAlign': 'right' });
                cB.css({ 'left': b+'px' });
            }

            // Killer function set
            this.killerFn = function( e ) {
                // Test if didn't clicked on the slideButton
                var tar = $(e.target);
                if (tar.parents( '.slideButton' ).size() === 0) {
                    me.locked   = false;
                    me.cB.removeClass( 'clicked' );
                    me.hide();
                    me.disableKillerFn();
                }
            };

            // Stores slideButton instance in container
            c.data( 'slideButton', me );

            // Event listeners
            cB.hover( me.show, me.hide );
            cB.click( me.click );
        },

        setOptions: function(options) {
            var o = this.options;
            var c = this.container;

            // Uses container height if no options.height is given
            if(typeof(o.height) === 'undefined' || o.height == null || o.height <= 0)
                if(c.height()) o.height = c.height();

            $.extend(o, options);
        },

        // Slide visualization functions
        show: function() {
            var me;
            if( typeof this.container !== 'undefined' )
                me = this.cB;
            else
                me = $(this);

            // Gets the instance
            var sb = me.parent().data( 'slideButton' );
            if(sb.deployed) return; // It is already deployed
            var o  = sb.options;

            var slidelem = me.prev();
            // Animates the contents of control
            slidelem.stop().animate( { 'width': parseInt(o['deployedWidth']) + 'px' }, o['inSpeed'] ); //, o['inAnimation'] );
            // Animates the contents of content
            slidelem.find( 'span' ).stop( true, true ).fadeIn();

            // Updates the state
            sb.container.addClass('deployed');
            sb.deployed = true;

            // Triggers events
            if( typeof o.onDeploy === 'function' ) o.onDeploy(sb);
        },

        hide: function(force) {
            var me;
            if(typeof this.container !== 'undefined')
                me = this.cB;
            else
                me = $(this);

            if( typeof force !== 'object' && force == true ) me.removeClass('clicked');
            if( me.hasClass('clicked') ) return; // Ignore hide when in clicked (locked) state

            // Gets the instance
            var sb = me.parent().data( 'slideButton' );
            if(!sb.deployed) return; // Already hidden
            var o  = sb.options;

            var slidelem = me.prev();
            // Animates the contents of control
            slidelem.stop().animate( { 'width': parseInt(o.width)+'px' }, o['outSpeed'], o['outAnimation'] );
            // Animates the contents of content
            slidelem.find( 'span' ).stop( true, true ).fadeOut();

            // Updates the state
            sb.container.removeClass( 'deployed' );
            sb.deployed = false;
            sb.locked   = false;

            // Triggers events
            if( typeof o.onHide === 'function' ) o.onHide(sb);
        },

        click: function (ev) {
            var me = $(this);

            // Gets the instance
            var sb = me.parent().data( 'slideButton' );
            var o  = sb.options;
            if( me.hasClass( 'clicked' ) )
            {
                me.removeClass( 'clicked' );
                sb.hide();
                sb.disableKillerFn();
            }
            else
            {
                sb.show();
                sb.enableKillerFn();
                me.addClass( 'clicked' );
            }

            // Triggers events
            if( typeof o.onClick === 'function' ) o.onClick(sb);
        },

        lock: function() {
            var sb = $(this).data( 'slideButton' );
            if(!sb.deployed) sb.show();
            sb.enableKillerFn(); // Hooks the click event on the document to close
            sb.locked = true;
        },

        // Lock killer functions
        enableKillerFn: function() {
            var me = this;
            $(document).bind( 'click', me.killerFn );
        },

        disableKillerFn: function() {
            var me = this;
            $(document).unbind( 'click', me.killerFn );
        },

    };

}(jQuery));


// Autoinitializarion
$(document).ready(function() {
    if(slideButtonAutoload)
        $( '.slideButton' ).each( function ( index, elem ) {
            $(elem).slideButton( slideButtonDefaults );
        });
});


///////////////////////////////////////////////////////////////////////
/*
 * Border-radius jQuery cssHook
 * ==========================================================
 * (C) 2011 José Ramón Díaz - jrdiazweb@gmail.com
 *
 * Instead of using borderRadius cssHook from Brandon Aaron for example
 * (https://github.com/brandonaaron/jquery-cssHooks), I define a custom
 * cssHook for the borderRadius CSS property for the sake of simplicity
 * and to reduce other modules dependencies.
 *
 * If you prefer to use another cssHook from another source (Brandon's
 * one for example), just delete this and include the other cssHook.
 *
 * NOTE: cssHooks needs jQuery v1.4.3 or greater.
 */
(function( $ ){

    var div      = document.createElement('div'),
        divStyle = div.style;

    if ( !$.cssHooks ) {
        // if not, output an error message
        throw("jQuery 1.4.3 or above is required for this plugin to work");
        return;
    }

    div = null; // Avoids IE memory leaks

    $.support.borderRadius =
        divStyle.MozBorderRadius    === ''? 'MozBorderRadius'    :
       (divStyle.msBorderRadius     === ''? 'msBorderRadius'     :
       (divStyle.WebkitBorderRadius === ''? 'WebkitBorderRadius' :
       (divStyle.OBorderRadius      === ''? 'OBorderRadius'      :
       (divStyle.borderRadius       === ''? 'borderRadius'       :
        false))));

    // Border radius will be set only in border-radius compatible "borderRadius" browsers
    if ( $.support.borderRadius && $.support.slideBorderRadius !== "borderRadius" )
    {

        $.cssHooks["borderRadius"]    = {

                get: function( elem, computed, extra ) {
                    return $.css( elem, $.support.borderRadius );
                },
                set: function( elem, value) {
                    elem.style[$.support.borderRadius] = value;
                }

        };

    }

})(jQuery);
CSS
/**
 * Slide Button Styles.
 * ================================================
 * (C) 2011 José Ramón Díaz - jrdiazweb@gmail.com
 *
 * http://3nibbles.blogspot.com/2011/07/plugin-jquery-sliding-buttons.html
 *
 * Slide Button CSS styles. Note that rounded borders is a CSS3 feature,
 * so, it will be represented correctly in CSS3 ready browsers.
 *
 * Legal stuff
 *     You are free to use this CSS, but you must give credit or keep header intact.
 *     Please, tell me if you find it useful. An email will be enough.
 *     If you enhance this code or correct a bug, please, tell me.
 */
.slideButton                                   { font-weight: bold; font-size: 11px; font-family: Arial } /* Container                 */
  .slideButton a                               { text-decoration: none; }                                 /* a components containers   */

  .slideButton .button                         { background-color: #FFFFFF; color: #000000; text-align: center;
                                                 cursor: pointer; text-transform: uppercase; }            /* Control                   */
    .slideButton.deployed .button              { background-color: #9AFF66; color: #FFFFFF;  }            /* Control - Deployed state  */

  .slideButton .content                        { background-color: #36A300; color: #FFFFFF; }             /* Content                   */
    .slideButton.deployed .content             {  }                                                       /* Content - Deployed state  */

/* Depends on contents of the content component */
  .slideButton .button span                    { color: #9AFF66; }                                        /* Control spans             */
    .slideButton.deployed .button span         { color: #36A300; }                                        /* Control spans - Deployed  */
  .slideButton .content span span              { color: #9AFF66; }                                        /* Content spans             */
    .slideButton.deployed .content span        {  }                                                       /* Content spans - Deployed  */

No hay comentarios:

Publicar un comentario