javascript椭圆旋转相册实现代码
作者:
支持自动和手动两种模式:自动模式下自动旋转展示,手动模式下通过鼠标选择当前图片,或通过提供的接口选择上一张/下一张图片
功能说明:
1.支持自动和手动两种模式:自动模式下自动旋转展示,手动模式下通过鼠标选择当前图片,或通过提供的接口选择上一张/下一张图片。
2.可自行添加旋转的缓动模式,默认模式为:匀速,先快后慢,先慢后快。
3.可自定义旋转轨迹的宽和高。
4.支持IE6 7 8 9 10 firefox chrome等浏览器。
效果预览:
实现原理:
根据对图片在椭圆轨迹上的运动,动态改变缩放大小,实现立体的视觉效果。
代码分析:
init:function(id,options){
var defaultOptions={
width:600, //容器宽
height:200, //容器高
imgWidth:100, //图片宽
imgHeight:60, //图片高
maxScale:1.5, //最大缩放倍数
minScale:0.5, //最小缩放倍数
rotateSpeed:10 //运转速度
}
options=util.extend(defaultOptions,options);//参数设置
this.container=util.$(id);
this.width=options.width;
this.height=options.height;
imgWidth=this.imgWidth=options.imgWidth;
imgHeight=this.imgHeight=options.imgHeight;
this.maxScale=options.maxScale;
this.minScale=options.minScale;
scaleMargin=this.maxScale-this.minScale;
this.rotateSpeed=options.rotateSpeed;
this.imgs=util.$$('img',this.container);
this.setContainerSize(this.width,this.height);
initImgRC(this.imgs);
}
首先是初始化函数,里面有defaultOptions作为默认值,用户也可以传入自定义的值,这些参数值包括:容器宽、容器高、图片宽、图片高、最大缩放倍数,最小缩放倍数,旋转速度等。初始化之后,调用setContainerSize函数。
/* 设置容器尺寸 */
setContainerSize:function(width,height){
width=width||this.width;
height=height||this.height;
this.container.style.position='relative';
this.container.style.width=width+'px';
this.container.style.height=height+'px';
changeRotateWH.call(this,width,height);//改变容器尺寸后改变旋转轨迹
},
setContainerSize函数设置了容器的尺寸,容器尺寸的大小决定了旋转轨迹的大小,例如当我们设置容器的高等于宽时,轨迹变成一个圆形。容器尺寸设定后,调用函数changeRotateWH。
/* 改变椭圆旋转轨迹的横半轴长,竖半轴长*/
var changeRotateWH=function(width,height){
var halfScale=(this.maxScale-this.minScale)/2;//旋转到中间位置时的图片的缩放大小
rotate={};
rotate.originX=width/2;//旋转原点X轴坐标
rotate.originY=height/2;//旋转原点Y轴坐标
rotate.halfRotateWidth=(width-this.imgWidth)/2; //旋转横半轴长
rotate.halfRotateHeight=(height-this.imgHeight)/2; //旋转竖半轴长
}
changeRotateWH函数的作用是根据容器的尺寸,设定椭圆旋转轨迹的横半轴长和竖半轴长(程序里面的halfRotateWidth和halfRotateHeight,具体计算方法为:轨迹高=(容器高-图片高)/2,轨迹宽=(容器宽-图片宽)/2)),在高中数学中,我们学过椭圆的标准方程:(),这里的横半轴和竖半轴分别对应椭圆方程的a和b。由于这里是横轴较长的椭圆,所以a>b。
/* 设置图片旋转角和初始位置,大小 */
var initImgRC=function(imgs){
var len=imgs.length;
con=(2*Math.PI)/len;
for(var i=0;i<len;i++){
imgs[i].RC=i*con;
imgs[i].style.width=imgWidth+'px';
imgs[i].style.height=imgHeight+'px';
setImgPositionAndSize(imgs[i],0);
}
}
设置好椭圆的基本坐标系之后,我们可以根据图片的数量,把图片排列成一个椭圆的形状,首先我们可以通过 2π/图片数量 求得图片之间间隔所占的角度,然后把图片平均分布在椭圆轨迹上,此时所有图片就围成了一个椭圆的形状,到这里图片的初始分布状态就出来了,接下来的任务就是需要使图片沿着这个轨迹动起来。
/* 设置图片位置和大小的匀速变化 */
var setImgPositionAndSize=function(img,path,direction){
direction=direction||'CW';
var dir=direction=='CW'?-1:1;
img.RC+=(path*dir);
modifyImgAngle(img);
setImgSize(img);
}
该函数根据每张图片位置的不同,设置图片对应的尺寸,另外我们还需要传入一个参数:direction(值为CW(顺时针)或ACW(逆时针)),之后通过不断增加图片的RC属性(旋转角),使图片匀速自动旋转,这时自动旋转的旋转模式就ok了。
/* 修改图片旋转角度(保证在0-2pai之间) */
var modifyImgAngle=function(img){
(img.RC>(2*Math.PI))&&(img.RC-=2*Math.PI);
(img.RC<0)&&(img.RC+=2*Math.PI);
}
在图片旋转之前,我们可以对每张图片的角度做一个小小的修改,把旋转角限定在0-2π之间,方便后续的计算。
/* 设置图片大小和位置 */
var setImgSize=function(img){
var left=rotate.originX+rotate.halfRotateWidth*Math.cos(img.RC)-imgWidth/2;
var top=rotate.originY-rotate.halfRotateHeight*Math.sin(img.RC)-imgHeight/2;
var scale=minScale+scaleMargin*(rotate.halfRotateHeight-rotate.halfRotateHeight*Math.sin(img.RC))/(2*rotate.halfRotateHeight);//图片在该时刻的缩放比
img.style.cssText='position:absolute;left:'+left+'px;'
+'top:'+top+'px;'
+'width:'+imgWidth*scale+'px;'
+'height:'+imgHeight*scale+'px;'
+'z-index:'+Math.round(scale*100);
}
如何通过改变旋转角使图片按椭圆的轨迹旋转呢?我们可以再回过头看看之前的椭圆方程:(),由于需要处理的是旋转,所以我们希望把对x,y的处理转换成对旋转角度的处理,因此x,y坐标可以表示为:x=a*cosα , y=b*sinα 。图片的X坐标表示为:rotate.originX+rotate.halfRotateWidth*Math.cos(img.RC)-imgWidth/2(rotate.originX为原点X坐标,这里取容器的中心点),Y轴同理。之前说过图片缩放大小的依据是图片所处的位置,因此缩放比例scale的值则根据y坐标所占竖轴的长度进行计算。另外,层级关系z-index则根据scale的值进行计算,尺寸大得层级高,显示在前面。
/* 设置旋转模式(自动/手动)*/
setPattern:function(patternName,option){
option=option||{};
this.pattern=patternName;
var rotateSpeed=option.rotateSpeed||10;
this.path=Math.PI/1000*rotateSpeed;
(typeof timeId!='undefined')&&window.clearInterval(timeId);
if(patternName==='auto'){//自动模式 可传入旋转方向:option.rotateDir 旋转速度:option.rotateSpeed
var self=this;
var direction=option.rotateDir||'CW';//顺时针:CW 逆时针:ACW
removeImgsHandler(this.imgs);
timeId=window.setInterval(function(){
for(var i=0,len=self.imgs.length;i<len;i++){
setImgPositionAndSize(self.imgs[i],self.path,direction);
}
},20);
}
else if(patternName==='hand'){//手动模式,可传回调函数:option.onSelected 缓动模式:option.tween
var onSelected=option.onSelected||util.emptyFunction;
var tween=Tween[tween]||Tween['easeOut'];//缓动模式默认为easeout
removeImgsHandler(this.imgs);
(typeof timeId!='undefined')&&window.clearInterval(timeId);
timeId=undefined;
bindHandlerForImgs(this.imgs,this.path,tween,onSelected);
}
}
}
现在看看用户选择手动模式或者自动模式的接口:setPattern方法,该方法根据传入的字符串不同而选择不同的模式,“auto”为自动模式,该模式还可以传入自定义参数,包括旋转速度和旋转方向。传入“hand”则为手动模式,附加参数可以为手动选择图片后的回调函数,以及旋转的缓动模式。
var Tween = {//缓动类 默认提供三种缓动模式:linear easein easeout
linear: function(t,b,c,d,dir){ return c*t/d*dir + b; },
easeIn: function(t,b,c,d,dir){
return c*(t/=d)*t*dir + b;
},
easeOut: function(t,b,c,d,dir){
return -c *(t/=d)*(t-2)*dir + b;
}
};
以上就是缓动模式类,默认的三个模式分别为:匀速 先慢后快 先快后慢。用户可以调用addTweenFunction方法添加自己的缓动模式。
更多关于缓动的话题可以参考这两篇文章:
http://zengrong.net/post/1151.htm
http://www.cnblogs.com/cloudgamer/archive/2009/01/06/Tween.html
/* 添加缓动模式 */
addTweenFunction:function(name,func){
if(typeof func=='Function'||typeof func=='Object'){
Tween[name]=func;
}
},
添加缓动模式的参数可以为对象或方法,一次性添加同类型的一组缓动模式建议使用对象添加。
/* 为图片绑定点击事件处理程序 */
var bindHandlerForImgs=function(imgs,path,onSelected){
for(var i=0,len=imgs.length;i<len;i++){
imgs[i].handler=imgSelectedHandler(imgs,path,onSelected);
util.addEventHandler(imgs[i],'click',imgs[i].handler);
}
}
在手动模式下,首先要做的就是为图片绑定点击的事件处理程序,点击的图片沿着椭圆轨迹旋转移动到最前端,并且可以触发回调函数。
/* 图片选择事件处理程序 */
var imgSelectedHandler=function(imgs,path,tween,onSelected){
return function(eve){
eve=eve||window.event;
var dir;
var angle;
var target=eve.target||eve.srcElement;
var RC=target.RC;
if(RC>=Math.PI/2&&RC<=Math.PI*3/2){
dir='ACW';
angle=3*Math.PI/2-RC;
}
else{
dir='CW';
Math.sin(RC)>=0?angle=Math.PI/2+RC:angle=RC-3*Math.PI/2;
}
(typeof timeId!='undefined')&&window.clearInterval(timeId);
rotateAngle(imgs,angle,dir,tween,onSelected);
}
}
再看看手动模式下的核心函数,该函数作为事件处理程序,在点击选择图片后执行。首先判断所点击图片处在椭圆轨迹的左边还是右边,如果是左边,则旋转方向为逆时针,右边则为顺时针(为了符合最短移动路程的原则),之后调用 rotateAngle使图片移动相应角度。
/* 旋转指定角度 */
var rotateAngle=function(imgs,angle,dir,tween,onSelected){
var duration=1000;
var startTime=(new Date()).getTime();
dir=='CW'?dir=-1:dir=1;
for(var i=0,len=imgs.length;i<len;i++){
imgs[i].startAngle=imgs[i].RC;
}
timeId=window.setInterval(function(){
var now=(new Date()).getTime();
if((now-startTime)>=duration){
window.clearInterval(timeId);
timeId=undefined;
onSelected=onSelected||util.emptyFunction;
onSelected();//触发回调函数;
}
for(var i=0,len=imgs.length;i<len;i++){
var path=tween(now-startTime,imgs[i].startAngle,angle,duration,dir);//通过缓动公式计算新角度(RC)
setPos(imgs[i],path,dir);
}
},20);
}
rotateAngle函数首先确定了旋转所经历的时间,图片的初始角度和开始旋转的时间,然后把一切工作交给缓动函数来计算图片下一次的旋转角度,缓动函数可以是用户设置的,也可以使用默认的easeout(先快后慢)。如果有回调函数的话,可以在旋转结束后触发。
/* 选择上一幅图片 */
prePho:function(onSelected){
if(this.pattern=='hand'){
onSelected=onSelected||util.emptyFunction;
var tween=tween||Tween['easeOut'];
if(typeof timeId!='undefined'){
return;
}else{
rotateAngle(this.imgs,con,'ACW',tween,onSelected);
}
}
},
/* 选择下一幅图片 */
nextPho:function(onSelected){
if(this.pattern=='hand'){
onSelected=onSelected||util.emptyFunction;
var tween=tween||Tween['easeOut'];
if(typeof timeId!='undefined'){
return;
}else{
rotateAngle(this.imgs,con,'CW',tween,onSelected);
}
}
},
另外在手动模式下,提供选择上一张图片和下一张图片的接口,原理就是使所有图片的旋转角度为图片之间的夹角,上一张图片和下一张图片的旋转方向分别设置为逆时针和顺时针。
var rp=new rotatePhos('container');
rp.setPattern('auto',{rotateSpeed:10});//自动模式 旋转速度为10
rp.setPattern('hand');//手动模式
最后是调用方法初始化后需要设置旋转的模式。
说了一大堆不知道说清楚了没有,这里提供所有源码,有兴趣的童鞋可以看看哈~
源代码:
html:
<div id="wrap" style="background:black;width:650px; height:250px; padding-top:20px; padding-left:20px;">
<div id="container">
<img src="pp.jpg" />
<img src="pp.jpg"/>
<img src="pp.jpg"/>
<img src="pp.jpg"/>
<img src="pp.jpg"/>
<img src="pp.jpg"/>
<img src="pp.jpg" />
<img src="pp.jpg"/>
<img src="pp.jpg"/>
<img src="pp.jpg"/>
<img src="pp.jpg"/>
<img src="pp.jpg"/>
</div>
</div>
<p>
手动模式:<input id="select" type="radio" name="sel" value="手动模式" onclick="rp.setPattern('hand');" checked="checked"/>
自动模式:<input id="select" type="radio" name="sel" value="自动模式" onclick="rp.setPattern('auto');" />
</p>
<p>
<input id="pre" type="button" value="上一张" />
<input id="next" type="button" value="下一张"/>
</p>
JS:
var util = {
$: function(sId) { return document.getElementById(sId); },
$$:function(tagName,parent){parent=parent||document; return parent.getElementsByTagName(tagName);},
addEventHandler: function(elem, type, handler) {
if (elem.addEventListener) {
elem.addEventListener(type, handler, false);
}
else {
elem.attachEvent("on" + type, handler);
}
},
removeEventHandler: function(elem, type, handler) {
if (elem.removeEventListener) {
elem.removeEventListener(type, handler, false);
}
else {
elem.detachEvent("on" + type, handler);
}
},
getComputedStyle: function(elem) {
if (elem.currentStyle)
return elem.currentStyle;
else {
return document.defaultView.getComputedStyle(elem, null);
}
},
getElementsByClassName: function(className, parentElement) {
var elems = (parentElement || document.body).getElementsByTagName("*");
var result = [];
for (i = 0; j = elems[i]; i++) {
if ((" " + j.className + " ").indexOf(" " + className + " ") != -1) {
result.push(j);
}
}
return result;
},
extend: function(destination, source) {
for (var name in source) {
destination[name] = source[name];
}
return destination;
},
emptyFunction:function(){}
};
var rotatePhos=(function(){
var rp=function(id,options){
this.init(id,options);//初始化
}
rp.prototype=(function(){
var rotate;
var imgWidth;
var imgHeight;
var scaleMargin;
var con;
var handler;
var minScale;
var Tween = {//缓动类 默认提供三种缓动模式:linear easein easeout
linear: function(t,b,c,d,dir){ return c*t/d*dir + b; },
easeIn: function(t,b,c,d,dir){
return c*(t/=d)*t*dir + b;
},
easeOut: function(t,b,c,d,dir){
return -c *(t/=d)*(t-2)*dir + b;
}
};
/* 改变椭圆旋转轨迹的横半轴长,竖半轴长*/
var changeRotateWH=function(width,height){
var halfScale=(this.maxScale-this.minScale)/2;//旋转到中间位置时的图片的缩放大小
rotate={};
rotate.originX=width/2;//旋转原点X轴坐标
rotate.originY=height/2;//旋转原点Y轴坐标
rotate.halfRotateWidth=(width-this.imgWidth)/2; //旋转横半轴长
rotate.halfRotateHeight=(height-this.imgHeight)/2; //旋转竖半轴长
}
/* 设置图片旋转角和初始位置,大小 */
var initImgRC=function(imgs){
var len=imgs.length;
con=(2*Math.PI)/len;
for(var i=0;i<len;i++){
imgs[i].RC=i*con;
imgs[i].style.width=imgWidth+'px';
imgs[i].style.height=imgHeight+'px';
setImgPositionAndSize(imgs[i],0);
}
}
/* 设置图片大小和位置 */
var setImgSize=function(img){
var left=rotate.originX+rotate.halfRotateWidth*Math.cos(img.RC)-imgWidth/2;
var top=rotate.originY-rotate.halfRotateHeight*Math.sin(img.RC)-imgHeight/2;
var scale=minScale+scaleMargin*(rotate.halfRotateHeight-rotate.halfRotateHeight*Math.sin(img.RC))/(2*rotate.halfRotateHeight);//图片在该时刻的缩放比
img.style.cssText='position:absolute;left:'+left+'px;'
+'top:'+top+'px;'
+'width:'+imgWidth*scale+'px;'
+'height:'+imgHeight*scale+'px;'
+'z-index:'+Math.round(scale*100);
}
/* 设置图片位置和大小的匀速变化 */
var setImgPositionAndSize=function(img,path,direction){
direction=direction||'CW';
var dir=direction=='CW'?-1:1;
img.RC+=(path*dir);
modifyImgAngle(img);
setImgSize(img);
}
/* 修改图片旋转角度(保证在0-2pai之间) */
var modifyImgAngle=function(img){
(img.RC>(2*Math.PI))&&(img.RC-=2*Math.PI);
(img.RC<0)&&(img.RC+=2*Math.PI);
}
/* 设置图片的新位置 */
var setPos=function(img,path){
img.RC=path;
modifyImgAngle(img);
var left=rotate.originX+rotate.halfRotateWidth*Math.cos(img.RC)-imgWidth/2;
var top=rotate.originY-rotate.halfRotateHeight*Math.sin(img.RC)-imgHeight/2;
var scale=0.5+scaleMargin*(rotate.halfRotateHeight-rotate.halfRotateHeight*Math.sin(img.RC))/(2*rotate.halfRotateHeight);//图片在该时刻的缩放比
img.style.cssText='position:absolute;left:'+left+'px;'
+'top:'+top+'px;'
+'width:'+imgWidth*scale+'px;'
+'height:'+imgHeight*scale+'px;'
+'z-index:'+Math.round(scale*100);
}
/* 旋转指定角度 */
var rotateAngle=function(imgs,angle,dir,tween,onSelected){
var duration=1000;
var startTime=(new Date()).getTime();
dir=='CW'?dir=-1:dir=1;
for(var i=0,len=imgs.length;i<len;i++){
imgs[i].startAngle=imgs[i].RC;
}
timeId=window.setInterval(function(){
var now=(new Date()).getTime();
if((now-startTime)>=duration){
window.clearInterval(timeId);
timeId=undefined;
onSelected=onSelected||util.emptyFunction;
onSelected();//触发回调函数;
}
for(var i=0,len=imgs.length;i<len;i++){
var path=tween(now-startTime,imgs[i].startAngle,angle,duration,dir);//通过缓动公式计算新角度(RC)
setPos(imgs[i],path,dir);
}
},20);
}
/* 图片选择事件处理程序 */
var imgSelectedHandler=function(imgs,path,tween,onSelected){
return function(eve){
eve=eve||window.event;
var dir;
var angle;
var target=eve.target||eve.srcElement;
var RC=target.RC;
if(RC>=Math.PI/2&&RC<=Math.PI*3/2){
dir='ACW';
angle=3*Math.PI/2-RC;
}
else{
dir='CW';
Math.sin(RC)>=0?angle=Math.PI/2+RC:angle=RC-3*Math.PI/2;
}
(typeof timeId!='undefined')&&window.clearInterval(timeId);
rotateAngle(imgs,angle,dir,tween,onSelected);
}
}
/* 为图片绑定点击事件处理程序 */
var bindHandlerForImgs=function(imgs,path,onSelected){
for(var i=0,len=imgs.length;i<len;i++){
imgs[i].handler=imgSelectedHandler(imgs,path,onSelected);
util.addEventHandler(imgs[i],'click',imgs[i].handler);
}
}
/* 删除图片上的点击事件处理程序 */
var removeImgsHandler=function(imgs){
for(var i=0,len=imgs.length;i<len;i++){
if(imgs[i].handler){
util.removeEventHandler(imgs[i],'click',imgs[i].handler);
}
}
}
return{
/* 初始化 */
init:function(id,options){
var defaultOptions={
width:600, //容器宽
height:200, //容器高
imgWidth:100, //图片宽
imgHeight:60, //图片高
maxScale:1.5, //最大缩放倍数
minScale:0.5, //最小缩放倍数
rotateSpeed:10 //运转速度
}
options=util.extend(defaultOptions,options);//参数设置
this.container=util.$(id);
this.width=options.width;
this.height=options.height;
imgWidth=this.imgWidth=options.imgWidth;
imgHeight=this.imgHeight=options.imgHeight;
this.maxScale=options.maxScale;
minScale=this.minScale=options.minScale;
scaleMargin=this.maxScale-this.minScale;
this.rotateSpeed=options.rotateSpeed;
this.imgs=util.$$('img',this.container);
this.setContainerSize(this.width,this.height);
initImgRC(this.imgs);
},
/* 设置容器尺寸 */
setContainerSize:function(width,height){
width=width||this.width;
height=height||this.height;
this.container.style.position='relative';
this.container.style.width=width+'px';
this.container.style.height=height+'px';
changeRotateWH.call(this,width,height);//改变容器尺寸后改变旋转轨迹
},
/* 选择上一幅图片 */
prePho:function(onSelected){
if(this.pattern=='hand'){
onSelected=onSelected||util.emptyFunction;
var tween=tween||Tween['easeOut'];
if(typeof timeId!='undefined'){
return;
}else{
rotateAngle(this.imgs,con,'ACW',tween,onSelected);
}
}
},
/* 选择下一幅图片 */
nextPho:function(onSelected){
if(this.pattern=='hand'){
onSelected=onSelected||util.emptyFunction;
var tween=tween||Tween['easeOut'];
if(typeof timeId!='undefined'){
return;
}else{
rotateAngle(this.imgs,con,'CW',tween,onSelected);
}
}
},
/* 添加缓动模式 */
addTweenFunction:function(name,func){
if(typeof func=='Function'||typeof func=='Object'){
Tween[name]=func;
}
},
/* 设置旋转模式(自动/手动)*/
setPattern:function(patternName,option){
option=option||{};
this.pattern=patternName;
var rotateSpeed=option.rotateSpeed||10;
this.path=Math.PI/1000*rotateSpeed;
(typeof timeId!='undefined')&&window.clearInterval(timeId);
if(patternName==='auto'){//自动模式 可传入旋转方向:option.rotateDir 旋转速度:option.rotateSpeed
var self=this;
var direction=option.rotateDir||'CW';//顺时针:CW 逆时针:ACW
removeImgsHandler(this.imgs);
timeId=window.setInterval(function(){
for(var i=0,len=self.imgs.length;i<len;i++){
setImgPositionAndSize(self.imgs[i],self.path,direction);
}
},20);
}
else if(patternName==='hand'){//手动模式,可传回调函数:option.onSelected 缓动模式:option.tween
var onSelected=option.onSelected||util.emptyFunction;
var tween=Tween[tween]||Tween['easeOut'];//缓动模式默认为easeout
removeImgsHandler(this.imgs);
(typeof timeId!='undefined')&&window.clearInterval(timeId);
timeId=undefined;
bindHandlerForImgs(this.imgs,this.path,tween,onSelected);
}
}
}
})();
return rp;
})();
var rp=new rotatePhos('container');
//rp.setPattern('auto',{rotateSpeed:10});
rp.setPattern('hand');
document.getElementById('pre').onclick=function(){rp.prePho();};
document.getElementById('next').onclick=function(){rp.nextPho();};
完整的实现代码:
<div id="wrap" style="background: black; width: 800px; height: 350px; padding-top: 20px; padding-left: 20px; padding-right: 20px;">
<div id="container"><img src="/upload/201201/20120116231926539.jpg" alt="" /> <img src="/upload/201201/20120116231926632.jpg" alt="" /> <img src="/upload/201201/20120116231926661.jpg" alt="" /> <img src="/upload/201201/20120116231926763.jpg" alt="" /> <img src="/upload/201201/20120116231926174.jpg" alt="" /> <img src="/upload/201201/20120116231926604.jpg" alt="" /> <img src="/upload/201201/20120116231927431.jpg" alt="" /> <img src="/upload/201201/20120116231927666.JPG" alt="" /> <img src="/upload/201201/20120116231927424.jpg" alt="" /> <img src="/upload/201201/20120116231927108.jpg" alt="" /> <img src="/upload/201201/20120116231927843.jpg" alt="" /> <img src="/upload/201201/20120116231927662.bmp" alt="" /></div>
</div>
<p>手动模式:<input id="select" onclick="rp.setPattern('hand');" type="radio" name="sel" value="手动模式" /> 自动模式:<input id="select" onclick="rp.setPattern('auto');" type="radio" name="sel" value="自动模式" /></p>
<p><input id="pre" type="button" value="上一张" /> <input id="next" type="button" value="下一张" /></p>
<p>
<script type="text/javascript">// <![CDATA[
var rotatePhos = (function() {
var util = {
$: function(sId) { return document.getElementById(sId); },
$$: function(tagName, parent) { parent = parent || document; return parent.getElementsByTagName(tagName); },
addEventHandler: function(elem, type, handler) {
if (elem.addEventListener) {
elem.addEventListener(type, handler, false);
}
else {
elem.attachEvent("on" + type, handler);
}
},
removeEventHandler: function(elem, type, handler) {
if (elem.removeEventListener) {
elem.removeEventListener(type, handler, false);
}
else {
elem.detachEvent("on" + type, handler);
}
},
getComputedStyle: function(elem) {
if (elem.currentStyle)
return elem.currentStyle;
else {
return document.defaultView.getComputedStyle(elem, null);
}
},
emptyFunction: function() { },
getElementsByClassName: function(className, parentElement) {
var elems = (parentElement || document.body).getElementsByTagName("*");
var result = [];
for (i = 0; j = elems[i]; i++) {
if ((" " + j.className + " ").indexOf(" " + className + " ") != -1) {
result.push(j);
}
}
return result;
},
extend: function(destination, source) {
for (var name in source) {
destination[name] = source[name];
}
return destination;
}
};
var rp = function(id, options) {
this.init(id, options); //初始化
}
rp.prototype = (function() {
var rotate;
var imgWidth;
var imgHeight;
var scaleMargin;
var con;
var handler;
var Tween = {//缓动类 默认提供三种缓动模式:linear easein easeout
linear: function(t, b, c, d, dir) { return c * t / d * dir + b; },
easeIn: function(t, b, c, d, dir) {
return c * (t /= d) * t * dir + b;
},
easeOut: function(t, b, c, d, dir) {
return -c * (t /= d) * (t - 2) * dir + b;
}
};
/* 改变椭圆旋转轨迹的横半轴长,竖半轴长*/
var changeRotateWH = function(width, height) {
var halfScale = (this.maxScale - this.minScale) / 2; //旋转到中间位置时的图片的缩放大小
rotate = {};
rotate.originX = width / 2; //旋转原点X轴坐标
rotate.originY = height / 2; //旋转原点Y轴坐标
rotate.halfRotateWidth = (width - this.imgWidth) / 2; //旋转横半轴长
rotate.halfRotateHeight = (height - this.imgHeight) / 2; //旋转竖半轴长
}
/* 设置图片旋转角和初始位置,大小 */
var initImgRC = function(imgs) {
var len = imgs.length;
con = (2 * Math.PI) / len;
for (var i = 0; i < len; i++) {
imgs[i].RC = i * con;
imgs[i].style.width = imgWidth + 'px';
imgs[i].style.height = imgHeight + 'px';
setImgPositionAndSize(imgs[i], 0);
}
}
/* 设置图片大小 */
var setImgSize = function(img) {
var left = rotate.originX + rotate.halfRotateWidth * Math.cos(img.RC) - imgWidth / 2;
var top = rotate.originY - rotate.halfRotateHeight * Math.sin(img.RC) - imgHeight / 2;
var scale = 0.5 + scaleMargin * (rotate.halfRotateHeight - rotate.halfRotateHeight * Math.sin(img.RC)) / (2 * rotate.halfRotateHeight); //图片在该时刻的缩放比
img.style.cssText = 'position:absolute;left:' + left + 'px;'
+ 'top:' + top + 'px;'
+ 'width:' + imgWidth * scale + 'px;'
+ 'height:' + imgHeight * scale + 'px;'
+ 'cursor:pointer;'
+ 'z-index:' + Math.round(scale * 100);
}
/* 设置图片位置和大小的匀速变化 */
var setImgPositionAndSize = function(img, path, direction) {
direction = direction || 'CW';
var dir = direction == 'CW' ? -1 : 1;
img.RC += (path * dir);
modifyImgAngle(img);
setImgSize(img);
}
/* 修改图片旋转角度(保证在0-2pai之间) */
var modifyImgAngle = function(img) {
(img.RC > (2 * Math.PI)) && (img.RC -= 2 * Math.PI);
(img.RC < 0) && (img.RC += 2 * Math.PI);
}
/* 设置图片的新位置 */
var setPos = function(img, path) {
img.RC = path;
modifyImgAngle(img);
var left = rotate.originX + rotate.halfRotateWidth * Math.cos(img.RC) - imgWidth / 2;
var top = rotate.originY - rotate.halfRotateHeight * Math.sin(img.RC) - imgHeight / 2;
var scale = 0.5 + scaleMargin * (rotate.halfRotateHeight - rotate.halfRotateHeight * Math.sin(img.RC)) / (2 * rotate.halfRotateHeight); //图片在该时刻的缩放比
img.style.cssText = 'position:absolute;left:' + left + 'px;'
+ 'top:' + top + 'px;'
+ 'width:' + imgWidth * scale + 'px;'
+ 'height:' + imgHeight * scale + 'px;'
+ 'z-index:' + Math.round(scale * 100);
}
/* 旋转指定角度 */
var rotateAngle = function(imgs, angle, dir, tween, onSelected) {
var duration = 1000;
var startTime = (new Date()).getTime();
dir == 'CW' ? dir = -1 : dir = 1;
for (var i = 0, len = imgs.length; i < len; i++) {
imgs[i].startAngle = imgs[i].RC;
}
timeId = window.setInterval(function() {
var now = (new Date()).getTime();
if ((now - startTime) >= duration) {
window.clearInterval(timeId);
timeId = undefined;
onSelected = onSelected || util.emptyFunction;
onSelected(); //触发回调函数;
}
for (var i = 0, len = imgs.length; i < len; i++) {
var path = tween(now - startTime, imgs[i].startAngle, angle, duration, dir); //通过缓动公式计算新角度(RC)
setPos(imgs[i], path, dir);
}
}, 20);
}
/* 图片选择事件处理程序 */
var imgSelectedHandler = function(imgs, path, tween, onSelected) {
return function(eve) {
eve = eve || window.event;
var dir;
var angle;
var target = eve.target || eve.srcElement;
var RC = target.RC;
if (RC >= Math.PI / 2 && RC <= Math.PI * 3 / 2) {
dir = 'ACW';
angle = 3 * Math.PI / 2 - RC;
}
else {
dir = 'CW';
Math.sin(RC) >= 0 ? angle = Math.PI / 2 + RC : angle = RC - 3 * Math.PI / 2;
}
(typeof timeId != 'undefined') && window.clearInterval(timeId);
rotateAngle(imgs, angle, dir, tween, onSelected);
}
}
/* 为图片绑定点击事件处理程序 */
var bindHandlerForImgs = function(imgs, path, onSelected) {
for (var i = 0, len = imgs.length; i < len; i++) {
imgs[i].handler = imgSelectedHandler(imgs, path, onSelected);
util.addEventHandler(imgs[i], 'click', imgs[i].handler);
}
}
/* 删除图片上的点击事件处理程序 */
var removeImgsHandler = function(imgs) {
for (var i = 0, len = imgs.length; i < len; i++) {
if (imgs[i].handler) {
util.removeEventHandler(imgs[i], 'click', imgs[i].handler);
}
}
}
return {
/* 初始化 */
init: function(id, options) {
var defaultOptions = {
width: 700, //容器宽
height: 300, //容器高
imgWidth: 130, //图片宽
imgHeight: 80, //图片高
maxScale: 1.5, //最大缩放倍数
minScale: 0.5, //最小缩放倍数
rotateSpeed: 10 //运转速度
}
options = util.extend(defaultOptions, options); //参数设置
this.container = util.$(id);
this.width = options.width;
this.height = options.height;
imgWidth = this.imgWidth = options.imgWidth;
imgHeight = this.imgHeight = options.imgHeight;
this.maxScale = options.maxScale;
this.minScale = options.minScale;
scaleMargin = this.maxScale - this.minScale;
this.rotateSpeed = options.rotateSpeed;
this.imgs = util.$$('img', this.container);
this.setContainerSize(this.width, this.height);
initImgRC(this.imgs);
},
/* 设置容器尺寸 */
setContainerSize: function(width, height) {
width = width || this.width;
height = height || this.height;
this.container.style.position = 'relative';
this.container.style.width = width + 'px';
this.container.style.height = height + 'px';
changeRotateWH.call(this, width, height); //改变容器尺寸后改变旋转轨迹
},
/* 选择上一幅图片 */
prePho: function(onSelected) {
if (this.pattern == 'hand') {
onSelected = onSelected || util.emptyFunction;
var tween = tween || Tween['easeOut'];
if (typeof timeId != 'undefined') {
return;
} else {
rotateAngle(this.imgs, con, 'ACW', tween, onSelected);
}
}
},
/* 选择下一幅图片 */
nextPho: function(onSelected) {
if (this.pattern == 'hand') {
onSelected = onSelected || util.emptyFunction;
var tween = tween || Tween['easeOut'];
if (typeof timeId != 'undefined') {
return;
} else {
rotateAngle(this.imgs, con, 'CW', tween, onSelected);
}
}
},
/* 添加缓动模式 */
addTweenFunction: function(name, func) {
if (typeof func == 'Function' || typeof func == 'Object') {
Tween[name] = func;
}
},
/* 设置旋转模式(自动/手动)*/
setPattern: function(patternName, option) {
option = option || {};
this.pattern = patternName;
var rotateSpeed = option.rotateSpeed || 10;
this.path = Math.PI / 1000 * rotateSpeed;
(typeof timeId != 'undefined') && window.clearInterval(timeId);
if (patternName === 'auto') {//自动模式 可传入旋转方向:option.rotateDir 旋转速度:option.rotateSpeed
var self = this;
var direction = option.rotateDir || 'CW'; //顺时针:CW 逆时针:ACW
removeImgsHandler(this.imgs);
timeId = window.setInterval(function() {
for (var i = 0, len = self.imgs.length; i < len; i++) {
setImgPositionAndSize(self.imgs[i], self.path, direction);
}
}, 20);
}
else if (patternName === 'hand') {//手动模式,可传回调函数:option.onSelected 缓动模式:option.tween
var onSelected = option.onSelected || util.emptyFunction;
var tween = Tween[tween] || Tween['easeOut']; //缓动模式默认为easeout
removeImgsHandler(this.imgs);
(typeof timeId != 'undefined') && window.clearInterval(timeId);
timeId = undefined;
bindHandlerForImgs(this.imgs, this.path, tween, onSelected);
}
}
}
})();
return rp;
})();
var rp=new rotatePhos('container');
//rp.setPattern('auto',{rotateSpeed:10});
rp.setPattern('hand');
document.getElementById('pre').onclick=function(){rp.prePho();};
document.getElementById('next').onclick=function(){rp.nextPho();};
// ]]></script>
</p>
1.支持自动和手动两种模式:自动模式下自动旋转展示,手动模式下通过鼠标选择当前图片,或通过提供的接口选择上一张/下一张图片。
2.可自行添加旋转的缓动模式,默认模式为:匀速,先快后慢,先慢后快。
3.可自定义旋转轨迹的宽和高。
4.支持IE6 7 8 9 10 firefox chrome等浏览器。
效果预览:
实现原理:
根据对图片在椭圆轨迹上的运动,动态改变缩放大小,实现立体的视觉效果。
代码分析:
复制代码 代码如下:
init:function(id,options){
var defaultOptions={
width:600, //容器宽
height:200, //容器高
imgWidth:100, //图片宽
imgHeight:60, //图片高
maxScale:1.5, //最大缩放倍数
minScale:0.5, //最小缩放倍数
rotateSpeed:10 //运转速度
}
options=util.extend(defaultOptions,options);//参数设置
this.container=util.$(id);
this.width=options.width;
this.height=options.height;
imgWidth=this.imgWidth=options.imgWidth;
imgHeight=this.imgHeight=options.imgHeight;
this.maxScale=options.maxScale;
this.minScale=options.minScale;
scaleMargin=this.maxScale-this.minScale;
this.rotateSpeed=options.rotateSpeed;
this.imgs=util.$$('img',this.container);
this.setContainerSize(this.width,this.height);
initImgRC(this.imgs);
}
首先是初始化函数,里面有defaultOptions作为默认值,用户也可以传入自定义的值,这些参数值包括:容器宽、容器高、图片宽、图片高、最大缩放倍数,最小缩放倍数,旋转速度等。初始化之后,调用setContainerSize函数。
复制代码 代码如下:
/* 设置容器尺寸 */
setContainerSize:function(width,height){
width=width||this.width;
height=height||this.height;
this.container.style.position='relative';
this.container.style.width=width+'px';
this.container.style.height=height+'px';
changeRotateWH.call(this,width,height);//改变容器尺寸后改变旋转轨迹
},
setContainerSize函数设置了容器的尺寸,容器尺寸的大小决定了旋转轨迹的大小,例如当我们设置容器的高等于宽时,轨迹变成一个圆形。容器尺寸设定后,调用函数changeRotateWH。
复制代码 代码如下:
/* 改变椭圆旋转轨迹的横半轴长,竖半轴长*/
var changeRotateWH=function(width,height){
var halfScale=(this.maxScale-this.minScale)/2;//旋转到中间位置时的图片的缩放大小
rotate={};
rotate.originX=width/2;//旋转原点X轴坐标
rotate.originY=height/2;//旋转原点Y轴坐标
rotate.halfRotateWidth=(width-this.imgWidth)/2; //旋转横半轴长
rotate.halfRotateHeight=(height-this.imgHeight)/2; //旋转竖半轴长
}
changeRotateWH函数的作用是根据容器的尺寸,设定椭圆旋转轨迹的横半轴长和竖半轴长(程序里面的halfRotateWidth和halfRotateHeight,具体计算方法为:轨迹高=(容器高-图片高)/2,轨迹宽=(容器宽-图片宽)/2)),在高中数学中,我们学过椭圆的标准方程:(),这里的横半轴和竖半轴分别对应椭圆方程的a和b。由于这里是横轴较长的椭圆,所以a>b。
复制代码 代码如下:
/* 设置图片旋转角和初始位置,大小 */
var initImgRC=function(imgs){
var len=imgs.length;
con=(2*Math.PI)/len;
for(var i=0;i<len;i++){
imgs[i].RC=i*con;
imgs[i].style.width=imgWidth+'px';
imgs[i].style.height=imgHeight+'px';
setImgPositionAndSize(imgs[i],0);
}
}
设置好椭圆的基本坐标系之后,我们可以根据图片的数量,把图片排列成一个椭圆的形状,首先我们可以通过 2π/图片数量 求得图片之间间隔所占的角度,然后把图片平均分布在椭圆轨迹上,此时所有图片就围成了一个椭圆的形状,到这里图片的初始分布状态就出来了,接下来的任务就是需要使图片沿着这个轨迹动起来。
复制代码 代码如下:
/* 设置图片位置和大小的匀速变化 */
var setImgPositionAndSize=function(img,path,direction){
direction=direction||'CW';
var dir=direction=='CW'?-1:1;
img.RC+=(path*dir);
modifyImgAngle(img);
setImgSize(img);
}
该函数根据每张图片位置的不同,设置图片对应的尺寸,另外我们还需要传入一个参数:direction(值为CW(顺时针)或ACW(逆时针)),之后通过不断增加图片的RC属性(旋转角),使图片匀速自动旋转,这时自动旋转的旋转模式就ok了。
复制代码 代码如下:
/* 修改图片旋转角度(保证在0-2pai之间) */
var modifyImgAngle=function(img){
(img.RC>(2*Math.PI))&&(img.RC-=2*Math.PI);
(img.RC<0)&&(img.RC+=2*Math.PI);
}
在图片旋转之前,我们可以对每张图片的角度做一个小小的修改,把旋转角限定在0-2π之间,方便后续的计算。
复制代码 代码如下:
/* 设置图片大小和位置 */
var setImgSize=function(img){
var left=rotate.originX+rotate.halfRotateWidth*Math.cos(img.RC)-imgWidth/2;
var top=rotate.originY-rotate.halfRotateHeight*Math.sin(img.RC)-imgHeight/2;
var scale=minScale+scaleMargin*(rotate.halfRotateHeight-rotate.halfRotateHeight*Math.sin(img.RC))/(2*rotate.halfRotateHeight);//图片在该时刻的缩放比
img.style.cssText='position:absolute;left:'+left+'px;'
+'top:'+top+'px;'
+'width:'+imgWidth*scale+'px;'
+'height:'+imgHeight*scale+'px;'
+'z-index:'+Math.round(scale*100);
}
如何通过改变旋转角使图片按椭圆的轨迹旋转呢?我们可以再回过头看看之前的椭圆方程:(),由于需要处理的是旋转,所以我们希望把对x,y的处理转换成对旋转角度的处理,因此x,y坐标可以表示为:x=a*cosα , y=b*sinα 。图片的X坐标表示为:rotate.originX+rotate.halfRotateWidth*Math.cos(img.RC)-imgWidth/2(rotate.originX为原点X坐标,这里取容器的中心点),Y轴同理。之前说过图片缩放大小的依据是图片所处的位置,因此缩放比例scale的值则根据y坐标所占竖轴的长度进行计算。另外,层级关系z-index则根据scale的值进行计算,尺寸大得层级高,显示在前面。
复制代码 代码如下:
/* 设置旋转模式(自动/手动)*/
setPattern:function(patternName,option){
option=option||{};
this.pattern=patternName;
var rotateSpeed=option.rotateSpeed||10;
this.path=Math.PI/1000*rotateSpeed;
(typeof timeId!='undefined')&&window.clearInterval(timeId);
if(patternName==='auto'){//自动模式 可传入旋转方向:option.rotateDir 旋转速度:option.rotateSpeed
var self=this;
var direction=option.rotateDir||'CW';//顺时针:CW 逆时针:ACW
removeImgsHandler(this.imgs);
timeId=window.setInterval(function(){
for(var i=0,len=self.imgs.length;i<len;i++){
setImgPositionAndSize(self.imgs[i],self.path,direction);
}
},20);
}
else if(patternName==='hand'){//手动模式,可传回调函数:option.onSelected 缓动模式:option.tween
var onSelected=option.onSelected||util.emptyFunction;
var tween=Tween[tween]||Tween['easeOut'];//缓动模式默认为easeout
removeImgsHandler(this.imgs);
(typeof timeId!='undefined')&&window.clearInterval(timeId);
timeId=undefined;
bindHandlerForImgs(this.imgs,this.path,tween,onSelected);
}
}
}
现在看看用户选择手动模式或者自动模式的接口:setPattern方法,该方法根据传入的字符串不同而选择不同的模式,“auto”为自动模式,该模式还可以传入自定义参数,包括旋转速度和旋转方向。传入“hand”则为手动模式,附加参数可以为手动选择图片后的回调函数,以及旋转的缓动模式。
复制代码 代码如下:
var Tween = {//缓动类 默认提供三种缓动模式:linear easein easeout
linear: function(t,b,c,d,dir){ return c*t/d*dir + b; },
easeIn: function(t,b,c,d,dir){
return c*(t/=d)*t*dir + b;
},
easeOut: function(t,b,c,d,dir){
return -c *(t/=d)*(t-2)*dir + b;
}
};
以上就是缓动模式类,默认的三个模式分别为:匀速 先慢后快 先快后慢。用户可以调用addTweenFunction方法添加自己的缓动模式。
更多关于缓动的话题可以参考这两篇文章:
http://zengrong.net/post/1151.htm
http://www.cnblogs.com/cloudgamer/archive/2009/01/06/Tween.html
复制代码 代码如下:
/* 添加缓动模式 */
addTweenFunction:function(name,func){
if(typeof func=='Function'||typeof func=='Object'){
Tween[name]=func;
}
},
添加缓动模式的参数可以为对象或方法,一次性添加同类型的一组缓动模式建议使用对象添加。
复制代码 代码如下:
/* 为图片绑定点击事件处理程序 */
var bindHandlerForImgs=function(imgs,path,onSelected){
for(var i=0,len=imgs.length;i<len;i++){
imgs[i].handler=imgSelectedHandler(imgs,path,onSelected);
util.addEventHandler(imgs[i],'click',imgs[i].handler);
}
}
在手动模式下,首先要做的就是为图片绑定点击的事件处理程序,点击的图片沿着椭圆轨迹旋转移动到最前端,并且可以触发回调函数。
复制代码 代码如下:
/* 图片选择事件处理程序 */
var imgSelectedHandler=function(imgs,path,tween,onSelected){
return function(eve){
eve=eve||window.event;
var dir;
var angle;
var target=eve.target||eve.srcElement;
var RC=target.RC;
if(RC>=Math.PI/2&&RC<=Math.PI*3/2){
dir='ACW';
angle=3*Math.PI/2-RC;
}
else{
dir='CW';
Math.sin(RC)>=0?angle=Math.PI/2+RC:angle=RC-3*Math.PI/2;
}
(typeof timeId!='undefined')&&window.clearInterval(timeId);
rotateAngle(imgs,angle,dir,tween,onSelected);
}
}
再看看手动模式下的核心函数,该函数作为事件处理程序,在点击选择图片后执行。首先判断所点击图片处在椭圆轨迹的左边还是右边,如果是左边,则旋转方向为逆时针,右边则为顺时针(为了符合最短移动路程的原则),之后调用 rotateAngle使图片移动相应角度。
复制代码 代码如下:
/* 旋转指定角度 */
var rotateAngle=function(imgs,angle,dir,tween,onSelected){
var duration=1000;
var startTime=(new Date()).getTime();
dir=='CW'?dir=-1:dir=1;
for(var i=0,len=imgs.length;i<len;i++){
imgs[i].startAngle=imgs[i].RC;
}
timeId=window.setInterval(function(){
var now=(new Date()).getTime();
if((now-startTime)>=duration){
window.clearInterval(timeId);
timeId=undefined;
onSelected=onSelected||util.emptyFunction;
onSelected();//触发回调函数;
}
for(var i=0,len=imgs.length;i<len;i++){
var path=tween(now-startTime,imgs[i].startAngle,angle,duration,dir);//通过缓动公式计算新角度(RC)
setPos(imgs[i],path,dir);
}
},20);
}
rotateAngle函数首先确定了旋转所经历的时间,图片的初始角度和开始旋转的时间,然后把一切工作交给缓动函数来计算图片下一次的旋转角度,缓动函数可以是用户设置的,也可以使用默认的easeout(先快后慢)。如果有回调函数的话,可以在旋转结束后触发。
复制代码 代码如下:
/* 选择上一幅图片 */
prePho:function(onSelected){
if(this.pattern=='hand'){
onSelected=onSelected||util.emptyFunction;
var tween=tween||Tween['easeOut'];
if(typeof timeId!='undefined'){
return;
}else{
rotateAngle(this.imgs,con,'ACW',tween,onSelected);
}
}
},
/* 选择下一幅图片 */
nextPho:function(onSelected){
if(this.pattern=='hand'){
onSelected=onSelected||util.emptyFunction;
var tween=tween||Tween['easeOut'];
if(typeof timeId!='undefined'){
return;
}else{
rotateAngle(this.imgs,con,'CW',tween,onSelected);
}
}
},
另外在手动模式下,提供选择上一张图片和下一张图片的接口,原理就是使所有图片的旋转角度为图片之间的夹角,上一张图片和下一张图片的旋转方向分别设置为逆时针和顺时针。
复制代码 代码如下:
var rp=new rotatePhos('container');
rp.setPattern('auto',{rotateSpeed:10});//自动模式 旋转速度为10
rp.setPattern('hand');//手动模式
最后是调用方法初始化后需要设置旋转的模式。
说了一大堆不知道说清楚了没有,这里提供所有源码,有兴趣的童鞋可以看看哈~
源代码:
html:
复制代码 代码如下:
<div id="wrap" style="background:black;width:650px; height:250px; padding-top:20px; padding-left:20px;">
<div id="container">
<img src="pp.jpg" />
<img src="pp.jpg"/>
<img src="pp.jpg"/>
<img src="pp.jpg"/>
<img src="pp.jpg"/>
<img src="pp.jpg"/>
<img src="pp.jpg" />
<img src="pp.jpg"/>
<img src="pp.jpg"/>
<img src="pp.jpg"/>
<img src="pp.jpg"/>
<img src="pp.jpg"/>
</div>
</div>
<p>
手动模式:<input id="select" type="radio" name="sel" value="手动模式" onclick="rp.setPattern('hand');" checked="checked"/>
自动模式:<input id="select" type="radio" name="sel" value="自动模式" onclick="rp.setPattern('auto');" />
</p>
<p>
<input id="pre" type="button" value="上一张" />
<input id="next" type="button" value="下一张"/>
</p>
JS:
复制代码 代码如下:
var util = {
$: function(sId) { return document.getElementById(sId); },
$$:function(tagName,parent){parent=parent||document; return parent.getElementsByTagName(tagName);},
addEventHandler: function(elem, type, handler) {
if (elem.addEventListener) {
elem.addEventListener(type, handler, false);
}
else {
elem.attachEvent("on" + type, handler);
}
},
removeEventHandler: function(elem, type, handler) {
if (elem.removeEventListener) {
elem.removeEventListener(type, handler, false);
}
else {
elem.detachEvent("on" + type, handler);
}
},
getComputedStyle: function(elem) {
if (elem.currentStyle)
return elem.currentStyle;
else {
return document.defaultView.getComputedStyle(elem, null);
}
},
getElementsByClassName: function(className, parentElement) {
var elems = (parentElement || document.body).getElementsByTagName("*");
var result = [];
for (i = 0; j = elems[i]; i++) {
if ((" " + j.className + " ").indexOf(" " + className + " ") != -1) {
result.push(j);
}
}
return result;
},
extend: function(destination, source) {
for (var name in source) {
destination[name] = source[name];
}
return destination;
},
emptyFunction:function(){}
};
var rotatePhos=(function(){
var rp=function(id,options){
this.init(id,options);//初始化
}
rp.prototype=(function(){
var rotate;
var imgWidth;
var imgHeight;
var scaleMargin;
var con;
var handler;
var minScale;
var Tween = {//缓动类 默认提供三种缓动模式:linear easein easeout
linear: function(t,b,c,d,dir){ return c*t/d*dir + b; },
easeIn: function(t,b,c,d,dir){
return c*(t/=d)*t*dir + b;
},
easeOut: function(t,b,c,d,dir){
return -c *(t/=d)*(t-2)*dir + b;
}
};
/* 改变椭圆旋转轨迹的横半轴长,竖半轴长*/
var changeRotateWH=function(width,height){
var halfScale=(this.maxScale-this.minScale)/2;//旋转到中间位置时的图片的缩放大小
rotate={};
rotate.originX=width/2;//旋转原点X轴坐标
rotate.originY=height/2;//旋转原点Y轴坐标
rotate.halfRotateWidth=(width-this.imgWidth)/2; //旋转横半轴长
rotate.halfRotateHeight=(height-this.imgHeight)/2; //旋转竖半轴长
}
/* 设置图片旋转角和初始位置,大小 */
var initImgRC=function(imgs){
var len=imgs.length;
con=(2*Math.PI)/len;
for(var i=0;i<len;i++){
imgs[i].RC=i*con;
imgs[i].style.width=imgWidth+'px';
imgs[i].style.height=imgHeight+'px';
setImgPositionAndSize(imgs[i],0);
}
}
/* 设置图片大小和位置 */
var setImgSize=function(img){
var left=rotate.originX+rotate.halfRotateWidth*Math.cos(img.RC)-imgWidth/2;
var top=rotate.originY-rotate.halfRotateHeight*Math.sin(img.RC)-imgHeight/2;
var scale=minScale+scaleMargin*(rotate.halfRotateHeight-rotate.halfRotateHeight*Math.sin(img.RC))/(2*rotate.halfRotateHeight);//图片在该时刻的缩放比
img.style.cssText='position:absolute;left:'+left+'px;'
+'top:'+top+'px;'
+'width:'+imgWidth*scale+'px;'
+'height:'+imgHeight*scale+'px;'
+'z-index:'+Math.round(scale*100);
}
/* 设置图片位置和大小的匀速变化 */
var setImgPositionAndSize=function(img,path,direction){
direction=direction||'CW';
var dir=direction=='CW'?-1:1;
img.RC+=(path*dir);
modifyImgAngle(img);
setImgSize(img);
}
/* 修改图片旋转角度(保证在0-2pai之间) */
var modifyImgAngle=function(img){
(img.RC>(2*Math.PI))&&(img.RC-=2*Math.PI);
(img.RC<0)&&(img.RC+=2*Math.PI);
}
/* 设置图片的新位置 */
var setPos=function(img,path){
img.RC=path;
modifyImgAngle(img);
var left=rotate.originX+rotate.halfRotateWidth*Math.cos(img.RC)-imgWidth/2;
var top=rotate.originY-rotate.halfRotateHeight*Math.sin(img.RC)-imgHeight/2;
var scale=0.5+scaleMargin*(rotate.halfRotateHeight-rotate.halfRotateHeight*Math.sin(img.RC))/(2*rotate.halfRotateHeight);//图片在该时刻的缩放比
img.style.cssText='position:absolute;left:'+left+'px;'
+'top:'+top+'px;'
+'width:'+imgWidth*scale+'px;'
+'height:'+imgHeight*scale+'px;'
+'z-index:'+Math.round(scale*100);
}
/* 旋转指定角度 */
var rotateAngle=function(imgs,angle,dir,tween,onSelected){
var duration=1000;
var startTime=(new Date()).getTime();
dir=='CW'?dir=-1:dir=1;
for(var i=0,len=imgs.length;i<len;i++){
imgs[i].startAngle=imgs[i].RC;
}
timeId=window.setInterval(function(){
var now=(new Date()).getTime();
if((now-startTime)>=duration){
window.clearInterval(timeId);
timeId=undefined;
onSelected=onSelected||util.emptyFunction;
onSelected();//触发回调函数;
}
for(var i=0,len=imgs.length;i<len;i++){
var path=tween(now-startTime,imgs[i].startAngle,angle,duration,dir);//通过缓动公式计算新角度(RC)
setPos(imgs[i],path,dir);
}
},20);
}
/* 图片选择事件处理程序 */
var imgSelectedHandler=function(imgs,path,tween,onSelected){
return function(eve){
eve=eve||window.event;
var dir;
var angle;
var target=eve.target||eve.srcElement;
var RC=target.RC;
if(RC>=Math.PI/2&&RC<=Math.PI*3/2){
dir='ACW';
angle=3*Math.PI/2-RC;
}
else{
dir='CW';
Math.sin(RC)>=0?angle=Math.PI/2+RC:angle=RC-3*Math.PI/2;
}
(typeof timeId!='undefined')&&window.clearInterval(timeId);
rotateAngle(imgs,angle,dir,tween,onSelected);
}
}
/* 为图片绑定点击事件处理程序 */
var bindHandlerForImgs=function(imgs,path,onSelected){
for(var i=0,len=imgs.length;i<len;i++){
imgs[i].handler=imgSelectedHandler(imgs,path,onSelected);
util.addEventHandler(imgs[i],'click',imgs[i].handler);
}
}
/* 删除图片上的点击事件处理程序 */
var removeImgsHandler=function(imgs){
for(var i=0,len=imgs.length;i<len;i++){
if(imgs[i].handler){
util.removeEventHandler(imgs[i],'click',imgs[i].handler);
}
}
}
return{
/* 初始化 */
init:function(id,options){
var defaultOptions={
width:600, //容器宽
height:200, //容器高
imgWidth:100, //图片宽
imgHeight:60, //图片高
maxScale:1.5, //最大缩放倍数
minScale:0.5, //最小缩放倍数
rotateSpeed:10 //运转速度
}
options=util.extend(defaultOptions,options);//参数设置
this.container=util.$(id);
this.width=options.width;
this.height=options.height;
imgWidth=this.imgWidth=options.imgWidth;
imgHeight=this.imgHeight=options.imgHeight;
this.maxScale=options.maxScale;
minScale=this.minScale=options.minScale;
scaleMargin=this.maxScale-this.minScale;
this.rotateSpeed=options.rotateSpeed;
this.imgs=util.$$('img',this.container);
this.setContainerSize(this.width,this.height);
initImgRC(this.imgs);
},
/* 设置容器尺寸 */
setContainerSize:function(width,height){
width=width||this.width;
height=height||this.height;
this.container.style.position='relative';
this.container.style.width=width+'px';
this.container.style.height=height+'px';
changeRotateWH.call(this,width,height);//改变容器尺寸后改变旋转轨迹
},
/* 选择上一幅图片 */
prePho:function(onSelected){
if(this.pattern=='hand'){
onSelected=onSelected||util.emptyFunction;
var tween=tween||Tween['easeOut'];
if(typeof timeId!='undefined'){
return;
}else{
rotateAngle(this.imgs,con,'ACW',tween,onSelected);
}
}
},
/* 选择下一幅图片 */
nextPho:function(onSelected){
if(this.pattern=='hand'){
onSelected=onSelected||util.emptyFunction;
var tween=tween||Tween['easeOut'];
if(typeof timeId!='undefined'){
return;
}else{
rotateAngle(this.imgs,con,'CW',tween,onSelected);
}
}
},
/* 添加缓动模式 */
addTweenFunction:function(name,func){
if(typeof func=='Function'||typeof func=='Object'){
Tween[name]=func;
}
},
/* 设置旋转模式(自动/手动)*/
setPattern:function(patternName,option){
option=option||{};
this.pattern=patternName;
var rotateSpeed=option.rotateSpeed||10;
this.path=Math.PI/1000*rotateSpeed;
(typeof timeId!='undefined')&&window.clearInterval(timeId);
if(patternName==='auto'){//自动模式 可传入旋转方向:option.rotateDir 旋转速度:option.rotateSpeed
var self=this;
var direction=option.rotateDir||'CW';//顺时针:CW 逆时针:ACW
removeImgsHandler(this.imgs);
timeId=window.setInterval(function(){
for(var i=0,len=self.imgs.length;i<len;i++){
setImgPositionAndSize(self.imgs[i],self.path,direction);
}
},20);
}
else if(patternName==='hand'){//手动模式,可传回调函数:option.onSelected 缓动模式:option.tween
var onSelected=option.onSelected||util.emptyFunction;
var tween=Tween[tween]||Tween['easeOut'];//缓动模式默认为easeout
removeImgsHandler(this.imgs);
(typeof timeId!='undefined')&&window.clearInterval(timeId);
timeId=undefined;
bindHandlerForImgs(this.imgs,this.path,tween,onSelected);
}
}
}
})();
return rp;
})();
var rp=new rotatePhos('container');
//rp.setPattern('auto',{rotateSpeed:10});
rp.setPattern('hand');
document.getElementById('pre').onclick=function(){rp.prePho();};
document.getElementById('next').onclick=function(){rp.nextPho();};
完整的实现代码:
复制代码 代码如下:
<div id="wrap" style="background: black; width: 800px; height: 350px; padding-top: 20px; padding-left: 20px; padding-right: 20px;">
<div id="container"><img src="/upload/201201/20120116231926539.jpg" alt="" /> <img src="/upload/201201/20120116231926632.jpg" alt="" /> <img src="/upload/201201/20120116231926661.jpg" alt="" /> <img src="/upload/201201/20120116231926763.jpg" alt="" /> <img src="/upload/201201/20120116231926174.jpg" alt="" /> <img src="/upload/201201/20120116231926604.jpg" alt="" /> <img src="/upload/201201/20120116231927431.jpg" alt="" /> <img src="/upload/201201/20120116231927666.JPG" alt="" /> <img src="/upload/201201/20120116231927424.jpg" alt="" /> <img src="/upload/201201/20120116231927108.jpg" alt="" /> <img src="/upload/201201/20120116231927843.jpg" alt="" /> <img src="/upload/201201/20120116231927662.bmp" alt="" /></div>
</div>
<p>手动模式:<input id="select" onclick="rp.setPattern('hand');" type="radio" name="sel" value="手动模式" /> 自动模式:<input id="select" onclick="rp.setPattern('auto');" type="radio" name="sel" value="自动模式" /></p>
<p><input id="pre" type="button" value="上一张" /> <input id="next" type="button" value="下一张" /></p>
<p>
<script type="text/javascript">// <![CDATA[
var rotatePhos = (function() {
var util = {
$: function(sId) { return document.getElementById(sId); },
$$: function(tagName, parent) { parent = parent || document; return parent.getElementsByTagName(tagName); },
addEventHandler: function(elem, type, handler) {
if (elem.addEventListener) {
elem.addEventListener(type, handler, false);
}
else {
elem.attachEvent("on" + type, handler);
}
},
removeEventHandler: function(elem, type, handler) {
if (elem.removeEventListener) {
elem.removeEventListener(type, handler, false);
}
else {
elem.detachEvent("on" + type, handler);
}
},
getComputedStyle: function(elem) {
if (elem.currentStyle)
return elem.currentStyle;
else {
return document.defaultView.getComputedStyle(elem, null);
}
},
emptyFunction: function() { },
getElementsByClassName: function(className, parentElement) {
var elems = (parentElement || document.body).getElementsByTagName("*");
var result = [];
for (i = 0; j = elems[i]; i++) {
if ((" " + j.className + " ").indexOf(" " + className + " ") != -1) {
result.push(j);
}
}
return result;
},
extend: function(destination, source) {
for (var name in source) {
destination[name] = source[name];
}
return destination;
}
};
var rp = function(id, options) {
this.init(id, options); //初始化
}
rp.prototype = (function() {
var rotate;
var imgWidth;
var imgHeight;
var scaleMargin;
var con;
var handler;
var Tween = {//缓动类 默认提供三种缓动模式:linear easein easeout
linear: function(t, b, c, d, dir) { return c * t / d * dir + b; },
easeIn: function(t, b, c, d, dir) {
return c * (t /= d) * t * dir + b;
},
easeOut: function(t, b, c, d, dir) {
return -c * (t /= d) * (t - 2) * dir + b;
}
};
/* 改变椭圆旋转轨迹的横半轴长,竖半轴长*/
var changeRotateWH = function(width, height) {
var halfScale = (this.maxScale - this.minScale) / 2; //旋转到中间位置时的图片的缩放大小
rotate = {};
rotate.originX = width / 2; //旋转原点X轴坐标
rotate.originY = height / 2; //旋转原点Y轴坐标
rotate.halfRotateWidth = (width - this.imgWidth) / 2; //旋转横半轴长
rotate.halfRotateHeight = (height - this.imgHeight) / 2; //旋转竖半轴长
}
/* 设置图片旋转角和初始位置,大小 */
var initImgRC = function(imgs) {
var len = imgs.length;
con = (2 * Math.PI) / len;
for (var i = 0; i < len; i++) {
imgs[i].RC = i * con;
imgs[i].style.width = imgWidth + 'px';
imgs[i].style.height = imgHeight + 'px';
setImgPositionAndSize(imgs[i], 0);
}
}
/* 设置图片大小 */
var setImgSize = function(img) {
var left = rotate.originX + rotate.halfRotateWidth * Math.cos(img.RC) - imgWidth / 2;
var top = rotate.originY - rotate.halfRotateHeight * Math.sin(img.RC) - imgHeight / 2;
var scale = 0.5 + scaleMargin * (rotate.halfRotateHeight - rotate.halfRotateHeight * Math.sin(img.RC)) / (2 * rotate.halfRotateHeight); //图片在该时刻的缩放比
img.style.cssText = 'position:absolute;left:' + left + 'px;'
+ 'top:' + top + 'px;'
+ 'width:' + imgWidth * scale + 'px;'
+ 'height:' + imgHeight * scale + 'px;'
+ 'cursor:pointer;'
+ 'z-index:' + Math.round(scale * 100);
}
/* 设置图片位置和大小的匀速变化 */
var setImgPositionAndSize = function(img, path, direction) {
direction = direction || 'CW';
var dir = direction == 'CW' ? -1 : 1;
img.RC += (path * dir);
modifyImgAngle(img);
setImgSize(img);
}
/* 修改图片旋转角度(保证在0-2pai之间) */
var modifyImgAngle = function(img) {
(img.RC > (2 * Math.PI)) && (img.RC -= 2 * Math.PI);
(img.RC < 0) && (img.RC += 2 * Math.PI);
}
/* 设置图片的新位置 */
var setPos = function(img, path) {
img.RC = path;
modifyImgAngle(img);
var left = rotate.originX + rotate.halfRotateWidth * Math.cos(img.RC) - imgWidth / 2;
var top = rotate.originY - rotate.halfRotateHeight * Math.sin(img.RC) - imgHeight / 2;
var scale = 0.5 + scaleMargin * (rotate.halfRotateHeight - rotate.halfRotateHeight * Math.sin(img.RC)) / (2 * rotate.halfRotateHeight); //图片在该时刻的缩放比
img.style.cssText = 'position:absolute;left:' + left + 'px;'
+ 'top:' + top + 'px;'
+ 'width:' + imgWidth * scale + 'px;'
+ 'height:' + imgHeight * scale + 'px;'
+ 'z-index:' + Math.round(scale * 100);
}
/* 旋转指定角度 */
var rotateAngle = function(imgs, angle, dir, tween, onSelected) {
var duration = 1000;
var startTime = (new Date()).getTime();
dir == 'CW' ? dir = -1 : dir = 1;
for (var i = 0, len = imgs.length; i < len; i++) {
imgs[i].startAngle = imgs[i].RC;
}
timeId = window.setInterval(function() {
var now = (new Date()).getTime();
if ((now - startTime) >= duration) {
window.clearInterval(timeId);
timeId = undefined;
onSelected = onSelected || util.emptyFunction;
onSelected(); //触发回调函数;
}
for (var i = 0, len = imgs.length; i < len; i++) {
var path = tween(now - startTime, imgs[i].startAngle, angle, duration, dir); //通过缓动公式计算新角度(RC)
setPos(imgs[i], path, dir);
}
}, 20);
}
/* 图片选择事件处理程序 */
var imgSelectedHandler = function(imgs, path, tween, onSelected) {
return function(eve) {
eve = eve || window.event;
var dir;
var angle;
var target = eve.target || eve.srcElement;
var RC = target.RC;
if (RC >= Math.PI / 2 && RC <= Math.PI * 3 / 2) {
dir = 'ACW';
angle = 3 * Math.PI / 2 - RC;
}
else {
dir = 'CW';
Math.sin(RC) >= 0 ? angle = Math.PI / 2 + RC : angle = RC - 3 * Math.PI / 2;
}
(typeof timeId != 'undefined') && window.clearInterval(timeId);
rotateAngle(imgs, angle, dir, tween, onSelected);
}
}
/* 为图片绑定点击事件处理程序 */
var bindHandlerForImgs = function(imgs, path, onSelected) {
for (var i = 0, len = imgs.length; i < len; i++) {
imgs[i].handler = imgSelectedHandler(imgs, path, onSelected);
util.addEventHandler(imgs[i], 'click', imgs[i].handler);
}
}
/* 删除图片上的点击事件处理程序 */
var removeImgsHandler = function(imgs) {
for (var i = 0, len = imgs.length; i < len; i++) {
if (imgs[i].handler) {
util.removeEventHandler(imgs[i], 'click', imgs[i].handler);
}
}
}
return {
/* 初始化 */
init: function(id, options) {
var defaultOptions = {
width: 700, //容器宽
height: 300, //容器高
imgWidth: 130, //图片宽
imgHeight: 80, //图片高
maxScale: 1.5, //最大缩放倍数
minScale: 0.5, //最小缩放倍数
rotateSpeed: 10 //运转速度
}
options = util.extend(defaultOptions, options); //参数设置
this.container = util.$(id);
this.width = options.width;
this.height = options.height;
imgWidth = this.imgWidth = options.imgWidth;
imgHeight = this.imgHeight = options.imgHeight;
this.maxScale = options.maxScale;
this.minScale = options.minScale;
scaleMargin = this.maxScale - this.minScale;
this.rotateSpeed = options.rotateSpeed;
this.imgs = util.$$('img', this.container);
this.setContainerSize(this.width, this.height);
initImgRC(this.imgs);
},
/* 设置容器尺寸 */
setContainerSize: function(width, height) {
width = width || this.width;
height = height || this.height;
this.container.style.position = 'relative';
this.container.style.width = width + 'px';
this.container.style.height = height + 'px';
changeRotateWH.call(this, width, height); //改变容器尺寸后改变旋转轨迹
},
/* 选择上一幅图片 */
prePho: function(onSelected) {
if (this.pattern == 'hand') {
onSelected = onSelected || util.emptyFunction;
var tween = tween || Tween['easeOut'];
if (typeof timeId != 'undefined') {
return;
} else {
rotateAngle(this.imgs, con, 'ACW', tween, onSelected);
}
}
},
/* 选择下一幅图片 */
nextPho: function(onSelected) {
if (this.pattern == 'hand') {
onSelected = onSelected || util.emptyFunction;
var tween = tween || Tween['easeOut'];
if (typeof timeId != 'undefined') {
return;
} else {
rotateAngle(this.imgs, con, 'CW', tween, onSelected);
}
}
},
/* 添加缓动模式 */
addTweenFunction: function(name, func) {
if (typeof func == 'Function' || typeof func == 'Object') {
Tween[name] = func;
}
},
/* 设置旋转模式(自动/手动)*/
setPattern: function(patternName, option) {
option = option || {};
this.pattern = patternName;
var rotateSpeed = option.rotateSpeed || 10;
this.path = Math.PI / 1000 * rotateSpeed;
(typeof timeId != 'undefined') && window.clearInterval(timeId);
if (patternName === 'auto') {//自动模式 可传入旋转方向:option.rotateDir 旋转速度:option.rotateSpeed
var self = this;
var direction = option.rotateDir || 'CW'; //顺时针:CW 逆时针:ACW
removeImgsHandler(this.imgs);
timeId = window.setInterval(function() {
for (var i = 0, len = self.imgs.length; i < len; i++) {
setImgPositionAndSize(self.imgs[i], self.path, direction);
}
}, 20);
}
else if (patternName === 'hand') {//手动模式,可传回调函数:option.onSelected 缓动模式:option.tween
var onSelected = option.onSelected || util.emptyFunction;
var tween = Tween[tween] || Tween['easeOut']; //缓动模式默认为easeout
removeImgsHandler(this.imgs);
(typeof timeId != 'undefined') && window.clearInterval(timeId);
timeId = undefined;
bindHandlerForImgs(this.imgs, this.path, tween, onSelected);
}
}
}
})();
return rp;
})();
var rp=new rotatePhos('container');
//rp.setPattern('auto',{rotateSpeed:10});
rp.setPattern('hand');
document.getElementById('pre').onclick=function(){rp.prePho();};
document.getElementById('next').onclick=function(){rp.nextPho();};
// ]]></script>
</p>