|
|
/** * jquery.slitslider.js v1.1.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
*/
;( function( $, window, undefined ) { 'use strict';
/* * debouncedresize: special jQuery event that happens once after a window resize * * latest version and complete README available on Github: * https://github.com/louisremi/jquery-smartresize/blob/master/jquery.debouncedresize.js
* * Copyright 2011 @louis_remi * Licensed under the MIT license. */ var $event = $.event, $special, resizeTimeout;
$special = $event.special.debouncedresize = { setup: function() { $( this ).on( "resize", $special.handler ); }, teardown: function() { $( this ).off( "resize", $special.handler ); }, handler: function( event, execAsap ) { // Save the context
var context = this, args = arguments, dispatch = function() { // set correct event type
event.type = "debouncedresize"; $event.dispatch.apply( context, args ); };
if ( resizeTimeout ) { clearTimeout( resizeTimeout ); }
execAsap ? dispatch() : resizeTimeout = setTimeout( dispatch, $special.threshold ); }, threshold: 20 };
// global
var $window = $( window ), $document = $( document ), Modernizr = window.Modernizr;
$.Slitslider = function( options, element ) { this.$elWrapper = $( element ); this._init( options ); };
$.Slitslider.defaults = { // transitions speed
speed : 800, // if true the item's slices will also animate the opacity value
optOpacity : false, // amount (%) to translate both slices - adjust as necessary
translateFactor : 230, // maximum possible angle
maxAngle : 25, // maximum possible scale
maxScale : 2, // slideshow on / off
autoplay : false, // keyboard navigation
keyboard : true, // time between transitions
interval : 4000, // callbacks
onBeforeChange : function( slide, idx ) { return false; }, onAfterChange : function( slide, idx ) { return false; } };
$.Slitslider.prototype = {
_init : function( options ) { // options
this.options = $.extend( true, {}, $.Slitslider.defaults, options );
// https://github.com/twitter/bootstrap/issues/2870
this.transEndEventNames = { 'WebkitTransition' : 'webkitTransitionEnd', 'MozTransition' : 'transitionend', 'OTransition' : 'oTransitionEnd', 'msTransition' : 'MSTransitionEnd', 'transition' : 'transitionend' }; this.transEndEventName = this.transEndEventNames[ Modernizr.prefixed( 'transition' ) ]; // suport for css 3d transforms and css transitions
this.support = Modernizr.csstransitions && Modernizr.csstransforms3d; // the slider
this.$el = this.$elWrapper.children( '.sl-slider' ); // the slides
this.$slides = this.$el.children( '.sl-slide' ).hide(); // total slides
this.slidesCount = this.$slides.length; // current slide
this.current = 0; // control if it's animating
this.isAnimating = false; // get container size
this._getSize(); // layout
this._layout(); // load some events
this._loadEvents(); // slideshow
if( this.options.autoplay ) { this._startSlideshow(); }
}, // gets the current container width & height
_getSize : function() {
this.size = { width : this.$elWrapper.outerWidth( true ), height : this.$elWrapper.outerHeight( true ) };
}, _layout : function() { this.$slideWrapper = $( '<div class="sl-slides-wrapper" />' ); // wrap the slides
this.$slides.wrapAll( this.$slideWrapper ).each( function( i ) { var $slide = $( this ), // vertical || horizontal
orientation = $slide.data( 'orientation' ); $slide.addClass( 'sl-slide-' + orientation ) .children() .wrapAll( '<div class="sl-content-wrapper" />' ) .wrapAll( '<div class="sl-content" />' ); } ); // set the right size of the slider/slides for the current window size
this._setSize(); // show first slide
this.$slides.eq( this.current ).show(); }, _navigate : function( dir, pos ) { if( this.isAnimating || this.slidesCount < 2 ) { return false; }
this.isAnimating = true;
var self = this, $currentSlide = this.$slides.eq( this.current );
// if position is passed
if( pos !== undefined ) {
this.current = pos;
} // if not check the boundaries
else if( dir === 'next' ) {
this.current = this.current < this.slidesCount - 1 ? ++this.current : 0;
} else if( dir === 'prev' ) {
this.current = this.current > 0 ? --this.current : this.slidesCount - 1;
}
this.options.onBeforeChange( $currentSlide, this.current ); // next slide to be shown
var $nextSlide = this.$slides.eq( this.current ), // the slide we want to cut and animate
$movingSlide = ( dir === 'next' ) ? $currentSlide : $nextSlide, // the following are the data attrs set for each slide
configData = $movingSlide.data(), config = {}; config.orientation = configData.orientation || 'horizontal', config.slice1angle = configData.slice1Rotation || 0, config.slice1scale = configData.slice1Scale || 1, config.slice2angle = configData.slice2Rotation || 0, config.slice2scale = configData.slice2Scale || 1; this._validateValues( config ); var cssStyle = config.orientation === 'horizontal' ? { marginTop : -this.size.height / 2 } : { marginLeft : -this.size.width / 2 }, // default slide's slices style
resetStyle = { 'transform' : 'translate(0%,0%) rotate(0deg) scale(1)', opacity : 1 }, // slice1 style
slice1Style = config.orientation === 'horizontal' ? { 'transform' : 'translateY(-' + this.options.translateFactor + '%) rotate(' + config.slice1angle + 'deg) scale(' + config.slice1scale + ')' } : { 'transform' : 'translateX(-' + this.options.translateFactor + '%) rotate(' + config.slice1angle + 'deg) scale(' + config.slice1scale + ')' }, // slice2 style
slice2Style = config.orientation === 'horizontal' ? { 'transform' : 'translateY(' + this.options.translateFactor + '%) rotate(' + config.slice2angle + 'deg) scale(' + config.slice2scale + ')' } : { 'transform' : 'translateX(' + this.options.translateFactor + '%) rotate(' + config.slice2angle + 'deg) scale(' + config.slice2scale + ')' }; if( this.options.optOpacity ) { slice1Style.opacity = 0; slice2Style.opacity = 0; } // we are adding the classes sl-trans-elems and sl-trans-back-elems to the slide that is either coming "next"
// or going "prev" according to the direction.
// the idea is to make it more interesting by giving some animations to the respective slide's elements
//( dir === 'next' ) ? $nextSlide.addClass( 'sl-trans-elems' ) : $currentSlide.addClass( 'sl-trans-back-elems' );
$currentSlide.removeClass( 'sl-trans-elems' );
var transitionProp = { 'transition' : 'all ' + this.options.speed + 'ms ease-in-out' };
// add the 2 slices and animate them
$movingSlide.css( 'z-index', this.slidesCount ) .find( 'div.sl-content-wrapper' ) .wrap( $( '<div class="sl-content-slice" />' ).css( transitionProp ) ) .parent() .cond( dir === 'prev', function() { var slice = this; this.css( slice1Style ); setTimeout( function() { slice.css( resetStyle );
}, 50 ); }, function() { var slice = this; setTimeout( function() { slice.css( slice1Style );
}, 50 ); } ) .clone() .appendTo( $movingSlide ) .cond( dir === 'prev', function() { var slice = this; this.css( slice2Style ); setTimeout( function() {
$currentSlide.addClass( 'sl-trans-back-elems' );
if( self.support ) {
slice.css( resetStyle ).on( self.transEndEventName, function() {
self._onEndNavigate( slice, $currentSlide, dir );
} );
} else {
self._onEndNavigate( slice, $currentSlide, dir );
}
}, 50 ); }, function() { var slice = this; setTimeout( function() {
$nextSlide.addClass( 'sl-trans-elems' ); if( self.support ) {
slice.css( slice2Style ).on( self.transEndEventName, function() {
self._onEndNavigate( slice, $currentSlide, dir );
} );
} else {
self._onEndNavigate( slice, $currentSlide, dir );
}
}, 50 ); } ) .find( 'div.sl-content-wrapper' ) .css( cssStyle ); $nextSlide.show(); }, _validateValues : function( config ) { // OK, so we are restricting the angles and scale values here.
// This is to avoid the slices wrong sides to be shown.
// you can adjust these values as you wish but make sure you also ajust the
// paddings of the slides and also the options.translateFactor value and scale data attrs
if( config.slice1angle > this.options.maxAngle || config.slice1angle < -this.options.maxAngle ) { config.slice1angle = this.options.maxAngle; } if( config.slice2angle > this.options.maxAngle || config.slice2angle < -this.options.maxAngle ) { config.slice2angle = this.options.maxAngle; } if( config.slice1scale > this.options.maxScale || config.slice1scale <= 0 ) { config.slice1scale = this.options.maxScale; } if( config.slice2scale > this.options.maxScale || config.slice2scale <= 0 ) { config.slice2scale = this.options.maxScale; } if( config.orientation !== 'vertical' && config.orientation !== 'horizontal' ) { config.orientation = 'horizontal' } }, _onEndNavigate : function( $slice, $oldSlide, dir ) { // reset previous slide's style after next slide is shown
var $slide = $slice.parent(), removeClasses = 'sl-trans-elems sl-trans-back-elems'; // remove second slide's slice
$slice.remove(); // unwrap..
$slide.css( 'z-index', 1 ) .find( 'div.sl-content-wrapper' ) .unwrap(); // hide previous current slide
$oldSlide.hide().removeClass( removeClasses ); $slide.removeClass( removeClasses ); // now we can navigate again..
this.isAnimating = false; this.options.onAfterChange( $slide, this.current ); }, _setSize : function() { // the slider and content wrappers will have the window's width and height
var cssStyle = { width : this.size.width, height : this.size.height }; this.$el.css( cssStyle ).find( 'div.sl-content-wrapper' ).css( cssStyle ); }, _loadEvents : function() { var self = this; $window.on( 'debouncedresize.slitslider', function( event ) { // update size values
self._getSize(); // set the sizes again
self._setSize(); } );
if ( this.options.keyboard ) { $document.on( 'keydown.slitslider', function(e) {
var keyCode = e.keyCode || e.which, arrow = { left: 37, up: 38, right: 39, down: 40 };
switch (keyCode) { case arrow.left :
self._stopSlideshow(); self._navigate( 'prev' ); break; case arrow.right : self._stopSlideshow(); self._navigate( 'next' ); break;
}
} );
} }, _startSlideshow: function() {
var self = this;
this.slideshow = setTimeout( function() {
self._navigate( 'next' );
if ( self.options.autoplay ) {
self._startSlideshow();
}
}, this.options.interval );
}, _stopSlideshow: function() {
if ( this.options.autoplay ) {
clearTimeout( this.slideshow ); this.isPlaying = false; this.options.autoplay = false;
}
}, _destroy : function( callback ) { this.$el.off( '.slitslider' ).removeData( 'slitslider' ); $window.off( '.slitslider' ); $document.off( '.slitslider' ); this.$slides.each( function( i ) {
var $slide = $( this ), $content = $slide.find( 'div.sl-content' ).children();
$content.appendTo( $slide ); $slide.children( 'div.sl-content-wrapper' ).remove();
} ); this.$slides.unwrap( this.$slideWrapper ).hide(); this.$slides.eq( 0 ).show(); if( callback ) {
callback.call();
}
}, // public methos: adds more slides to the slider
add : function( $slides, callback ) {
this.$slides = this.$slides.add( $slides );
var self = this; $slides.each( function( i ) {
var $slide = $( this ), // vertical || horizontal
orientation = $slide.data( 'orientation' );
$slide.hide().addClass( 'sl-slide-' + orientation ) .children() .wrapAll( '<div class="sl-content-wrapper" />' ) .wrapAll( '<div class="sl-content" />' ) .end() .appendTo( self.$el.find( 'div.sl-slides-wrapper' ) );
} );
this._setSize();
this.slidesCount = this.$slides.length; if ( callback ) {
callback.call( $items );
}
}, // public method: shows next slide
next : function() {
this._stopSlideshow(); this._navigate( 'next' );
}, // public method: shows previous slide
previous : function() {
this._stopSlideshow(); this._navigate( 'prev' );
}, // public method: goes to a specific slide
jump : function( pos ) {
pos -= 1;
if( pos === this.current || pos >= this.slidesCount || pos < 0 ) {
return false;
}
this._stopSlideshow(); this._navigate( pos > this.current ? 'next' : 'prev', pos );
}, // public method: starts the slideshow
// any call to next(), previous() or jump() will stop the slideshow
play : function() {
if( !this.isPlaying ) {
this.isPlaying = true;
this._navigate( 'next' ); this.options.autoplay = true; this._startSlideshow();
}
}, // public method: pauses the slideshow
pause : function() {
if( this.isPlaying ) {
this._stopSlideshow();
}
}, // public method: check if isAnimating is true
isActive : function() {
return this.isAnimating;
}, // publicc methos: destroys the slicebox instance
destroy : function( callback ) {
this._destroy( callback ); }
}; var logError = function( message ) {
if ( window.console ) {
window.console.error( message ); }
}; $.fn.slitslider = function( options ) {
var self = $.data( this, 'slitslider' ); if ( typeof options === 'string' ) { var args = Array.prototype.slice.call( arguments, 1 ); this.each(function() { if ( !self ) {
logError( "cannot call methods on slitslider prior to initialization; " + "attempted to call method '" + options + "'" ); return; } if ( !$.isFunction( self[options] ) || options.charAt(0) === "_" ) {
logError( "no such method '" + options + "' for slitslider self" ); return; } self[ options ].apply( self, args ); }); } else { this.each(function() { if ( self ) {
self._init(); } else {
self = $.data( this, 'slitslider', new $.Slitslider( options, this ) ); }
}); } return self; }; } )( jQuery, window );
|