View as "text/plain" | Blame | Last modification | View Log | RSS feed
/** Cloud 9 Carousel 2.2.0** Pseudo-3D carousel plugin for jQuery/Zepto focused on performance.** Based on the original CloudCarousel by R. Cecco.** See the demo and download the latest version:* http://specious.github.io/cloud9carousel/** Copyright (c) 2017 by Ildar Sagdejev ( http://specious.github.io )* Copyright (c) 2011 by R. Cecco ( http://www.professorcloud.com )** MIT License** Please retain this copyright header in all versions of the software** Requires:* - jQuery >= 1.3.0 or Zepto >= 1.1.1** Optional (jQuery only):* - Reflection support via reflection.js plugin by Christophe Beyls* http://www.digitalia.be/software/reflectionjs-for-jquery* - Mousewheel support via mousewheel plugin* http://plugins.jquery.com/mousewheel/*/;(function($) {//// Detect CSS transform support//var transform = (function() {var vendors = ['webkit', 'moz', 'ms'];var style = document.createElement( "div" ).style;var trans = 'transform' in style ? 'transform' : undefined;for( var i = 0, count = vendors.length; i < count; i++ ) {var prop = vendors[i] + 'Transform';if( prop in style ) {trans = prop;break;}}return trans;})();var Item = function( element, options ) {element.item = this;this.element = element;if( element.tagName === 'IMG' ) {this.fullWidth = element.width;this.fullHeight = element.height;} else {element.style.display = "inline-block";this.fullWidth = element.offsetWidth;this.fullHeight = element.offsetHeight;}element.style.position = 'absolute';if( options.mirror && this.element.tagName === 'IMG' ) {// Wrap image in a div together with its generated reflectionthis.reflection = $(element).reflect( options.mirror ).next()[0];var $reflection = $(this.reflection);this.reflection.fullHeight = $reflection.height();$reflection.css( 'margin-top', options.mirror.gap + 'px' );$reflection.css( 'width', '100%' );element.style.width = "100%";// The item element now contains the image and reflectionthis.element = this.element.parentNode;this.element.item = this;this.element.alt = element.alt;this.element.title = element.title;}if( transform && options.transforms )this.element.style[transform + "Origin"] = "0 0";this.moveTo = function( x, y, scale ) {this.width = this.fullWidth * scale;this.height = this.fullHeight * scale;this.x = x;this.y = y;this.scale = scale;var style = this.element.style;style.zIndex = "" + (scale * 100) | 0;if( transform && options.transforms ) {style[transform] = "translate(" + x + "px, " + y + "px) scale(" + scale + ")";} else {// Manually resize the gap between the image and its reflectionif( options.mirror && this.element.tagName === 'IMG' )this.reflection.style.marginTop = (options.mirror.gap * scale) + "px";style.width = this.width + "px";style.left = x + "px";style.top = y + "px";}}}var time = !window.performance || !window.performance.now ?function() { return +new Date() } :function() { return performance.now() };//// Detect requestAnimationFrame() support//// Support legacy browsers:// http://www.paulirish.com/2011/requestanimationframe-for-smart-animating///var cancelFrame = window.cancelAnimationFrame || window.cancelRequestAnimationFrame;var requestFrame = window.requestAnimationFrame;(function() {var vendors = ['webkit', 'moz', 'ms'];for( var i = 0, count = vendors.length; i < count && !cancelFrame; i++ ) {cancelFrame = window[vendors[i]+'CancelAnimationFrame'] || window[vendors[i]+'CancelRequestAnimationFrame'];requestFrame = requestFrame && window[vendors[i]+'RequestAnimationFrame'];}}());var Carousel = function( element, options ) {var self = this;var $container = $(element);this.items = [];this.xOrigin = (options.xOrigin === null) ? $container.width() * 0.5 : options.xOrigin;this.yOrigin = (options.yOrigin === null) ? $container.height() * 0.1 : options.yOrigin;this.xRadius = (options.xRadius === null) ? $container.width() / 2.3 : options.xRadius;this.yRadius = (options.yRadius === null) ? $container.height() / 6 : options.yRadius;this.farScale = options.farScale;this.rotation = this.destRotation = Math.PI/2; // start with the first item positioned in frontthis.speed = options.speed;this.smooth = options.smooth;this.fps = options.fps;this.timer = 0;this.autoPlayAmount = options.autoPlay;this.autoPlayDelay = options.autoPlayDelay;this.autoPlayTimer = 0;this.frontItemClass = options.frontItemClass;this.onLoaded = options.onLoaded;this.onRendered = options.onRendered;this.onAnimationFinished = options.onAnimationFinished;this.itemOptions = {transforms: options.transforms}if( options.mirror ) {this.itemOptions.mirror = $.extend( { gap: 2 }, options.mirror );}$container.css( { position: 'relative', overflow: 'hidden' } );// Rotation:// * 0 : right// * Pi/2 : front// * Pi : left// * 3 Pi/2 : backthis.renderItem = function( itemIndex, rotation ) {var item = this.items[itemIndex];var sin = Math.sin(rotation);var farScale = this.farScale;var scale = farScale + ((1-farScale) * (sin+1) * 0.5);item.moveTo(this.xOrigin + (scale * ((Math.cos(rotation) * this.xRadius) - (item.fullWidth * 0.5))),this.yOrigin + (scale * sin * this.yRadius),scale);return item;}this.render = function() {var count = this.items.length;var spacing = 2 * Math.PI / count;var radians = this.rotation;var nearest = this.nearestIndex();for( var i = 0; i < count; i++ ) {var item = this.renderItem( i, radians );if( i === nearest )$(item.element).addClass( this.frontItemClass );else$(item.element).removeClass( this.frontItemClass );radians += spacing;}if( typeof this.onRendered === 'function' )this.onRendered( this );}this.playFrame = function() {var rem = self.destRotation - self.rotation;var now = time();var dt = (now - self.lastTime) * 0.002;self.lastTime = now;if( Math.abs(rem) < 0.003 ) {self.rotation = self.destRotation;self.pause();if( typeof self.onAnimationFinished === 'function' )self.onAnimationFinished();} else {// Asymptotically approach the destinationself.rotation = self.destRotation - rem / (1 + (self.speed * dt));self.scheduleNextFrame();}self.render();}this.scheduleNextFrame = function() {this.lastTime = time();this.timer = this.smooth && cancelFrame ?requestFrame( self.playFrame ) :setTimeout( self.playFrame, 1000 / this.fps );}this.itemsRotated = function() {return this.items.length * ((Math.PI/2) - this.rotation) / (2*Math.PI);}this.floatIndex = function() {var count = this.items.length;var floatIndex = this.itemsRotated() % count;// Make sure float-index is positivereturn (floatIndex < 0) ? floatIndex + count : floatIndex;}this.nearestIndex = function() {return Math.round( this.floatIndex() ) % this.items.length;}this.nearestItem = function() {return this.items[this.nearestIndex()];}this.play = function() {if( this.timer === 0 )this.scheduleNextFrame();}this.pause = function() {this.smooth && cancelFrame ? cancelFrame( this.timer ) : clearTimeout( this.timer );this.timer = 0;}//// Spin the carousel by (+-) count items//this.go = function( count ) {this.destRotation += (2 * Math.PI / this.items.length) * count;this.play();}this.goTo = function( index ) {var count = this.items.length;// Find the shortest way to rotate item to frontvar diff = index - (this.floatIndex() % count);if( 2 * Math.abs(diff) > count )diff -= (diff > 0) ? count : -count;// Halt any rotation already in progressthis.destRotation = this.rotation;// Spin the opposite way to bring item to frontthis.go( -diff );// Return rotational distance (in items) to the targetreturn diff;}this.deactivate = function() {this.pause();clearInterval( this.autoPlayTimer );if( options.buttonLeft ) options.buttonLeft.unbind( 'click' );if( options.buttonRight ) options.buttonRight.unbind( 'click' );$container.unbind( '.cloud9' );}this.autoPlay = function() {this.autoPlayTimer = setInterval(function() { self.go( self.autoPlayAmount ) },this.autoPlayDelay);}this.enableAutoPlay = function() {// Stop auto-play on mouse over$container.bind( 'mouseover.cloud9', function() {clearInterval( self.autoPlayTimer );} );// Resume auto-play when mouse leaves the container$container.bind( 'mouseout.cloud9', function() {self.autoPlay();} );this.autoPlay();}this.bindControls = function() {if( options.buttonLeft ) {options.buttonLeft.bind( 'click', function() {self.go( -1 );return false;} );}if( options.buttonRight ) {options.buttonRight.bind( 'click', function() {self.go( 1 );return false;} );}if( options.mouseWheel ) {$container.bind( 'mousewheel.cloud9', function( event, delta ) {self.go( (delta > 0) ? 1 : -1 );return false;} );}if( options.bringToFront ) {$container.bind( 'click.cloud9', function( event ) {var hits = $(event.target).closest( '.' + options.itemClass );if( hits.length !== 0 ) {var diff = self.goTo( self.items.indexOf( hits[0].item ) );// Suppress default browser action if the item isn't roughly in frontif( Math.abs(diff) > 0.5 )event.preventDefault();}} );}}var items = $container.find( '.' + options.itemClass );this.finishInit = function() {//// Wait until all images have completely loaded//for( var i = 0; i < items.length; i++ ) {var item = items[i];if( (item.tagName === 'IMG') &&((item.width === undefined) || ((item.complete !== undefined) && !item.complete)) )return;}clearInterval( this.initTimer );// Init itemsfor( i = 0; i < items.length; i++ )this.items.push( new Item( items[i], this.itemOptions ) );// Disable click-dragging of items$container.bind( 'mousedown onselectstart', function() { return false } );if( this.autoPlayAmount !== 0 ) this.enableAutoPlay();this.bindControls();this.render();if( typeof this.onLoaded === 'function' )this.onLoaded( this );};this.initTimer = setInterval( function() { self.finishInit() }, 50 );}//// The jQuery plugin//$.fn.Cloud9Carousel = function( options ) {return this.each( function() {/* For full list of options see the README */options = $.extend( {xOrigin: null, // null: calculated automaticallyyOrigin: null,xRadius: null,yRadius: null,farScale: 0.5, // scale of the farthest itemtransforms: true, // enable CSS transformssmooth: true, // enable smooth animation via requestAnimationFrame()fps: 30, // fixed frames per second (if smooth animation is off)speed: 4, // positive numberautoPlay: 0, // [ 0: off | number of items (integer recommended, positive is clockwise) ]autoPlayDelay: 4000,bringToFront: false,itemClass: 'cloud9-item',frontItemClass: null,handle: 'carousel'}, options );$(this).data( options.handle, new Carousel( this, options ) );} );}})( window.jQuery || window.Zepto );