cloud9carousel是一款与jQuery和Zepto配合使用、支持多个轮播实例、支持任何HTML元素、速度流畅、专注于性能的3D透视旋转木马插件,使用requestAnimationFrame方法固定FPS来实现平滑过渡动画、自动开启GPU来支持CSS 转换动画(自动判断是否支持),有onLoaded、onRendered、onAnimationFinished三个回调函数,支持元素倒影(需要reflection.js插件)、自动轮播、鼠标滚轮切换、点击切换。
演示:http://specious.github.io/cloud9carousel/species.html
依赖jquery插件:
- jQuery 1.3.0+版本或Zepto 1.1.1+版本(必需)
- 作者Christophe Beyls的reflection.js插件可以实现镜像倒影效果(可选,仅限jQuery)
- mousewheel.js插件可以实现鼠标滚轮切换效果(可选,仅限jQuery)
通过依赖的插件可以实现更多的效果。
使用教程
HTML代码:
<div id="carousel"> <img class="cloud9-item" src="images/1.png" alt="Item #1"> <img class="cloud9-item" src="images/2.png" alt="Item #2"> <img class="cloud9-item" src="images/3.png" alt="Item #3"> <img class="cloud9-item" src="images/4.png" alt="Item #4"> <img class="cloud9-item" src="images/5.png" alt="Item #5"> <img class="cloud9-item" src="images/6.png" alt="Item #6"> </div> <div id="buttons"> <button class="left"> ← </button> <button class="right"> → </button> </div>
提示:也可以是img以外的元素,主要是class类名要跟设置的一样
CSS代码:
#carousel .cloud9-item, #buttons button {
cursor: pointer;
}
提示:左右切换按钮的样式,可以自定义。
Javascript代码:
$("#carousel").Cloud9Carousel({
buttonLeft: $("#buttons > .left"),
buttonRight: $("#buttons > .right"),
autoPlay: 1,
bringToFront: true
});
更多参数:
上面是插件示例,可以通过下面的参数进行更多的配置
- xOrigin - 旋转木马X轴的中心点,默认值为容器宽度的一半(container width / 2)。
- yOrigin - 旋转木马Y轴的中心点,默认值为容器宽度的10分之1(container height / 10)。
- xRadius - 旋转木马宽度的一半(即X轴半径),默认值为容器宽度的2.3分之1(container width / 2.3)。
- yRadius - 旋转木马高度的一半(即Y轴半径),默认值为容器宽度的6分之1(container height / 6)。
- farScale - 旋转木马中最远一项的缩放,范围0-1,默认值0.5.
- mirror - 倒影插件Reflection.js的配置项目,看下文示例,默认值none。
- transforms - 如果浏览器支持CSS3的transforms转换属性则使用它,默认true。
- smooth - 如果浏览器支持requestAnimationFrame API就启用,实现平滑过渡效果
- fps - 每一秒的动画帧数(如果smooth被禁用生效)
- speed - 旋转木马的相对速度系数,取值范围:任何正数(即1~),数值越大速度越快,如1表示慢,4表示中等,10表示快,默认值为4,可根据自己的喜好进行调整
- autoPlay - 旋转木马自动轮播,正数顺时针轮播,负数逆时针轮播,0关闭自动轮播,默认值为0。(注意:当鼠标悬停在转盘容器上时,不会执行自动播放)
- autoPlayDelay - 自动轮播的延迟时间,单位为毫秒,默认4000。
- mouseWheel - 使用鼠标滚轮来旋转,依赖mousewheel.js插件,可设置为true开启,默认值为false。
- bringToFront - 点击旋转木马其实的某一项将会旋转到最前面,默认为false。
- buttonLeft - 元素的jQuery选择器,使旋转木马左边的元素旋转到最前面(逆时针旋转),默认值为none。
- buttonRight - 元素的jQuery选择器,使旋转木马右边的元素旋转到最前面(顺时针旋转),默认值为none。
- itemClass - 旋转木马每一项的class,如果配置了该项,需要把HTML代码中的class修改,默认值"cloud9-item"。
- frontItemClass - 旋转木马最前面那一项的class,默认值为none。
- handle - 旋转木马方法要用到的句柄,如:$("#carousel").data("carousel").go(1)
插件方法
插件提供有几种常见需求的方法
- go(count) - 旋转到指定数量的项,count是数量值(+为顺时针旋转,-为逆时针旋转)。
- goTo(index) - 用于旋转到特定索引的项,index是索引值。
- nearestIndex() - 返回最靠近前面基于0的项的索引(整数),默认值为none。
- nearestItem() - 返回对最靠近前面的项的对象(Item object),默认值为none。
- deactivate() - 禁用旋转木马,可以用来停止旋转木马并从中释放旋转木马元素,以便于在不受旋转木马干扰的情况下操作里面的元素。
- itemsRotated() - 返回从初始0位置旋转的项目的内插数,在有5个项目的旋转木马中,顺时针旋转三圈,其值将为-15,如果转盘进一步旋转到下一个项目的一半,则值为-15.5(float),默认值为none。
- floatIndex() - 返回转盘前面项目“索引”的插值。例如,如果旋转木马经过项目2的20%到达下一个项目,那么将返回2.2(float)。
方法示例
$("#carousel").data("carousel").go( 3 );
提示:其中的carousel是插件handle参数定义的,carousel是默认值,也可以定义其它的。
回调函数
- onLoaded - 插件初始化后执行,只执行一次
- onRendered - 每次在帧完成计算后执行
- onAnimationFinished - 轮播结束后执行
代码示例:
// Hide carousel while items are loading
$("#carousel").css( 'visibility', 'hidden' ).Cloud9Carousel( {
bringToFront: true,
onLoaded: function(carousel) {
// Show carousel
$(carousel).css( 'visibility', 'visible' );
alert( 'Carousel is ready!' );
},
onRendered: function(carousel) {
//var item = $(carousel).data("carousel").nearestItem();//官方示例,好像是错的
var item = carousel.nearestItem();
console.log( "Item closest to the front: " + $(item).attr("alt") );
}
onAnimationFinished: function(carousel){
/**/
}
});
实现镜像倒影
引入reflection.js插件后,在插件的配置参数mirror可以设置倒影效果,代码如下:
mirror: {
gap: 12, /* 12 pixel gap between item and reflection */
height: 0.2, /* 20% of item height */
opacity: 0.4 /* 40% opacity at the top */
}
参数说明
- gap - 倒影和项目之间的垂直间距,单位像素,默认值为2。
- height - 倒影高度相对于项目高度的比例,范围0-1,默认为1/3。
- opacity - 倒影的透明度,范围0-1,默认为0.5。
Github地址:https://github.com/specious/cloud9carousel
Cloud 9 Carousel 2.2.0版本完整代码:
/*
* 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 reflection
this.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 reflection
this.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 reflection
if( 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 front
this.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 : back
this.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 destination
self.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 positive
return (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 front
var diff = index - (this.floatIndex() % count);
if( 2 * Math.abs(diff) > count )
diff -= (diff > 0) ? count : -count;
// Halt any rotation already in progress
this.destRotation = this.rotation;
// Spin the opposite way to bring item to front
this.go( -diff );
// Return rotational distance (in items) to the target
return 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 front
if( 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 items
for( 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 automatically
yOrigin: null,
xRadius: null,
yRadius: null,
farScale: 0.5, // scale of the farthest item
transforms: true, // enable CSS transforms
smooth: true, // enable smooth animation via requestAnimationFrame()
fps: 30, // fixed frames per second (if smooth animation is off)
speed: 4, // positive number
autoPlay: 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 );
提示:Github如果打不开,可以把上面的完整代码复制保存为cloud9carousel.js文件引用



