/** * 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 = $( '
' ); // 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( '' ) .wrapall( '' ); } ); // 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( $( '' ).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( '' ) .wrapall( '' ) .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 );