/**
* jquery.baraja.js v1.0.0 * http://www.codrops.com * * Licensed under the MIT license. * http://www.opensource.org/licenses/mit-license.php * * Copyright 2012, Codrops * http://www.codrops.com */
jQuery.fn.reverse = [].reverse;
- ( function( $, window, undefined ) {
'use strict';
// global var Modernizr = window.Modernizr;
$.Baraja = function( options, element ) {
this.$el = $( element ); this._init( options );
};
// the options $.Baraja.defaults = { // if we want to specify a selector that triggers the next() function. example: '#baraja-nav-next' nextEl : , // if we want to specify a selector that triggers the previous() function prevEl : , // default transition speed speed : 300, // default transition easing easing : 'ease-in-out' };
$.Baraja.prototype = {
_init : function( options ) {
// options this.options = $.extend( true, {}, $.Baraja.defaults, options );
var transEndEventNames = { 'WebkitTransition' : 'webkitTransitionEnd', 'MozTransition' : 'transitionend', 'OTransition' : 'oTransitionEnd', 'msTransition' : 'MSTransitionEnd', 'transition' : 'transitionend' }; this.transEndEventName = transEndEventNames[ Modernizr.prefixed( 'transition' ) ];
this._setDefaultFanSettings();
this.$items = this.$el.children( 'li' ); this.itemsCount = this.$items.length; if( this.itemsCount === 0 ) { return false; } // support for CSS Transitions this.supportTransitions = Modernizr.csstransitions; // opened/closed deck this.closed = true; // lowest value for the z-index given to the items this.itemZIndexMin = 1000; // sets the item's z-index value this._setStack(); // initialize some events this._initEvents();
}, _setDefaultFanSettings : function() {
this.fanSettings = { // speed for opening/closing speed : 500, // easing for opening/closing easing : 'ease-out', // difference/range of possible angles that the items will have // example: with range:90 and center:false the first item // will have 0deg and the last one 90deg; // if center:true, then the first one will have 45deg // and the last one -45deg; in both cases the difference is 90deg range : 90, // this defines the position of the first item // (to the right, to the left) // and its angle (clockwise / counterclockwise) direction : 'right', // transform origin: // you can also pass a minX and maxX, meaning the left value // will vary between minX and maxX origin : { x : 25, y : 100 }, // additional translation of each item translation : 0, // if the cards should be centered after the transform // is applied center : true, // add a random factor to the final transform scatter : false };
}, _validateDefaultFanSettings : function( settings ) {
if( !settings.origin ) { settings.origin = this.fanSettings.origin; } else { settings.origin.x = settings.origin.x || this.fanSettings.origin.x; settings.origin.y = settings.origin.y || this.fanSettings.origin.y; } settings.speed = settings.speed || this.fanSettings.speed; settings.easing = settings.easing || this.fanSettings.easing; settings.direction = settings.direction || this.fanSettings.direction; settings.range = settings.range || this.fanSettings.range; settings.translation = settings.translation || this.fanSettings.translation; if( settings.center == undefined ) { settings.center = this.fanSettings.center } if( settings.scatter == undefined ) { settings.scatter = this.fanSettings.scatter }
this.direction = settings.direction;
return settings;
}, _setStack : function( $items ) {
var self = this; $items = $items || this.$items;
$items.each( function( i ) {
$( this ).css( 'z-index', self.itemZIndexMin + self.itemsCount - 1 - i );
} );
}, _updateStack : function( $el, dir ) {
var currZIndex = Number( $el.css( 'z-index' ) ), newZIndex = dir === 'next' ? this.itemZIndexMin - 1 : this.itemZIndexMin + this.itemsCount, extra = dir === 'next' ? '+=1' : '-=1';
$el.css( 'z-index', newZIndex );
this.$items.filter( function() {
var zIdx = Number( $( this ).css( 'z-index' ) ), cond = dir === 'next' ? zIdx < currZIndex : zIdx > currZIndex
return cond;
} ).css( 'z-index', extra );
}, _initEvents : function() {
var self = this;
if( this.options.nextEl !== ) {
$( this.options.nextEl ).on( 'click.baraja', function() {
self._navigate( 'next' ); return false;
} );
}
if( this.options.prevEl !== ) {
$( this.options.prevEl ).on( 'click.baraja', function() {
self._navigate( 'prev' ); return false;
} );
}
this.$el.on( 'click.baraja', 'li', function() {
if( !self.isAnimating ) {
self._move2front( $( this ) );
}
} );
}, _resetTransition : function( $el ) {
$el.css( { '-webkit-transition' : 'none', '-moz-transition' : 'none', '-ms-transition' : 'none', '-o-transition' : 'none', 'transition' : 'none' } );
}, _setOrigin : function( $el, x, y ) {
$el.css( 'transform-origin' , x + '% ' + y + '%' );
}, _setTransition : function( $el, prop, speed, easing, delay ) {
if( !this.supportTransitions ) { return false; } if( !prop ) { prop = 'all'; } if( !speed ) { speed = this.options.speed; } if( !easing ) { easing = this.options.easing; } if( !delay ) { delay = 0; }
var styleCSS = ;
prop === 'transform' ? styleCSS = { '-webkit-transition' : '-webkit-transform ' + speed + 'ms ' + easing + ' ' + delay + 'ms', '-moz-transition' : '-moz-transform ' + speed + 'ms ' + easing + ' ' + delay + 'ms', '-ms-transition' : '-ms-transform ' + speed + 'ms ' + easing + ' ' + delay + 'ms', '-o-transition' : '-o-transform ' + speed + 'ms ' + easing + ' ' + delay + 'ms', 'transition' : 'transform ' + speed + 'ms ' + easing + ' ' + delay + 'ms' } : styleCSS = { '-webkit-transition' : prop + ' ' + speed + 'ms ' + easing + ' ' + delay + 'ms', '-moz-transition' : prop + ' ' + speed + 'ms ' + easing + ' ' + delay + 'ms', '-ms-transition' : prop + ' ' + speed + 'ms ' + easing + ' ' + delay + 'ms', '-o-transition' : prop + ' ' + speed + 'ms ' + easing + ' ' + delay + 'ms', 'transition' : prop + ' ' + speed + 'ms ' + easing + ' ' + delay + 'ms' }
$el.css( styleCSS );
}, _applyTransition : function( $el, styleCSS, fncomplete, force ) {
if( this.supportTransitions ) {
if( fncomplete ) {
$el.on( this.transEndEventName, fncomplete );
if( force ) { fncomplete.call(); }
}
setTimeout( function() { $el.css( styleCSS ); }, 25 );
} else {
$el.css( styleCSS );
if( fncomplete ) {
fncomplete.call();
}
}
}, _navigate : function( dir ) {
this.closed = false;
var self = this, extra = 15, cond = dir === 'next' ? self.itemZIndexMin + self.itemsCount - 1 : self.itemZIndexMin, $item = this.$items.filter( function() {
return Number( $( this ).css( 'z-index' ) ) === cond;
} ), translation = dir === 'next' ? $item.outerWidth( true ) + extra : $item.outerWidth( true ) * -1 - extra, rotation = dir === 'next' ? 5 : 5 * -1;
this._setTransition( $item, 'transform', this.options.speed, this.options.easing );
this._applyTransition( $item, { transform : 'translate(' + translation + 'px) rotate(' + rotation + 'deg)' }, function() {
$item.off( self.transEndEventName ); self._updateStack( $item, dir );
self._applyTransition( $item, { transform : 'translate(0px) rotate(0deg)' }, function() {
$item.off( self.transEndEventName ); self.isAnimating = false; self.closed = true;
} );
} );
}, _move2front : function( $item ) {
this.isAnimating = true;
var self = this, isTop = Number( $item.css( 'z-index' ) ) === this.itemZIndexMin + this.itemsCount - 1, callback = isTop ? function() { self.isAnimating = false; } : function() { return false; }, $item = isTop ? null : $item;
// if it's the one with higher z-index, just close the baraja if( !this.closed ) {
this._close( callback, $item );
} else {
this._fan();
}
if( isTop ) { return false; }
this._resetTransition( $item ); this._setOrigin( $item, 50, 50 );
$item.css( { opacity : 0, transform : 'scale(2) translate(100px) rotate(20deg)' } );
this._updateStack( $item, 'prev' );
setTimeout( function() {
self._setTransition( $item, 'all', self.options.speed, 'ease-in' ); self._applyTransition( $item, { transform : 'none', opacity : 1 }, function() {
$item.off( self.transEndEventName ); self.isAnimating = false;
} );
}, this.options.speed / 2 );
}, _close : function( callback, $item ) {
var self = this, $items = self.$items, force = this.closed ? true : false;
if( $item ) { $items = $items.not( $item ); }
this._applyTransition( $items, { transform : 'none' }, function() {
self.closed = true; $items.off( self.transEndEventName ); self._resetTransition( $items ); setTimeout(function() {
self._setOrigin( $items, 50, 50 );
if( callback ) { callback.call(); }
}, 25);
}, force );
}, _fan : function( settings ) {
var self = this;
this.closed = false;
settings = this._validateDefaultFanSettings( settings || {} );
// set transform origins // if minX and maxX are passed: if( settings.origin.minX && settings.origin.maxX ) {
var max = settings.origin.maxX, min = settings.origin.minX, stepOrigin = ( max - min ) / this.itemsCount;
this.$items.each( function( i ) {
var $el = $( this ), pos = self.itemsCount - 1 - ( Number( $el.css( 'z-index' ) ) - self.itemZIndexMin ), originX = pos * ( max - min + stepOrigin ) / self.itemsCount + min;
if( settings.direction === 'left' ) {
originX = max + min - originX;
}
self._setOrigin( $( this ), originX, settings.origin.y );
} );
} else {
this._setOrigin( this.$items, settings.origin.x , settings.origin.y );
}
this._setTransition( this.$items, 'transform', settings.speed, settings.easing );
var stepAngle = settings.range / ( this.itemsCount - 1 ), stepTranslation = settings.translation / ( this.itemsCount - 1 ), cnt = 0;
this.$items.each( function( i ) {
var $el = $( this ), pos = self.itemsCount - 1 - ( Number( $el.css( 'z-index' ) ) - self.itemZIndexMin ), val = settings.center ? settings.range / 2 : settings.range, angle = val - stepAngle * pos, position = stepTranslation * ( self.itemsCount - pos - 1 );
if( settings.direction === 'left' ) {
angle *= -1; position *= -1;
}
if( settings.scatter ) {
var extraAngle = Math.floor( Math.random() * stepAngle ), extraPosition = Math.floor( Math.random() * stepTranslation ) ;
// not for the first item.. if( pos !== self.itemsCount - 1 ) {
angle = settings.direction === 'left' ? angle + extraAngle : angle - extraAngle; position = settings.direction === 'left' ? position - extraPosition : position + extraPosition;
}
}
// save.. $el.data( { translation : position, rotation : angle } );
self._applyTransition( $el, { transform : 'translate(' + position + 'px) rotate(' + angle + 'deg)' }, function() {
++cnt; $el.off( self.transEndEventName );
if( cnt === self.itemsCount - 1 ) { self.isAnimating = false; }
} );
} );
}, // adds new elements to the deck _add : function( $elems ) {
var self = this, newElemsCount = $elems.length, cnt = 0;
$elems.css( 'opacity', 0 ).appendTo( this.$el );
// reset this.$items = this.$el.children( 'li' ); this.itemsCount = this.$items.length;
// set z-indexes this._setStack( $elems );
// animate new items $elems.css( 'transform', 'scale(1.8) translate(200px) rotate(15deg)' ).reverse().each( function( i ) {
var $el = $( this );
self._setTransition( $el, 'all', 500, 'ease-out', i * 200 ); self._applyTransition( $el, { transform : 'none', opacity : 1 }, function() {
++cnt;
$el.off( self.transEndEventName ); self._resetTransition( $el );
if( cnt === newElemsCount ) { self.isAnimating = false; }
} );
} );
}, _allowAction : function() {
return this.itemsCount > 1;
}, _prepare : function( callback ) {
var self = this;
if( !this.closed ) {
this._close( function() {
callback.call();
} );
} else {
callback.call();
}
}, _dispatch : function( action, args ) {
var self = this;
if( ( ( action === this._fan || action === this._navigate ) && !this._allowAction() ) || this.isAnimating ) { return false; }
this.isAnimating = true;
this._prepare( function() {
action.call( self, args );
} );
}, // public method: closes the deck close : function( settings ) {
if( this.isAnimating ) { return false; } this._close();
}, // public method: shows next item next : function() {
this._dispatch( this._navigate, 'next' );
}, // public method: shows previous item previous : function() {
this._dispatch( this._navigate, 'prev' );
}, // public method: opens the deck fan : function( settings ) {
this._dispatch( this._fan, settings );
}, // public method: adds new elements add : function ( $elems ) {
this._dispatch( this._add, $elems );
}
};
var logError = function( message ) {
if ( window.console ) {
window.console.error( message );
}
};
$.fn.baraja = function( options ) {
var instance = $.data( this, 'baraja' );
if ( typeof options === 'string' ) {
var args = Array.prototype.slice.call( arguments, 1 );
this.each(function() {
if ( !instance ) {
logError( "cannot call methods on baraja prior to initialization; " + "attempted to call method '" + options + "'" ); return;
}
if ( !$.isFunction( instance[options] ) || options.charAt(0) === "_" ) {
logError( "no such method '" + options + "' for baraja instance" ); return;
}
instance[ options ].apply( instance, args );
});
} else {
this.each(function() {
if ( instance ) {
instance._init();
} else {
instance = $.data( this, 'baraja', new $.Baraja( options, this ) );
}
});
}
return instance;
};
} )( jQuery, window );