javascript椭圆旋转相册实现代码


Posted in Javascript onJanuary 16, 2012

功能说明:
1.支持自动和手动两种模式:自动模式下自动旋转展示,手动模式下通过鼠标选择当前图片,或通过提供的接口选择上一张/下一张图片。
2.可自行添加旋转的缓动模式,默认模式为:匀速,先快后慢,先慢后快。
3.可自定义旋转轨迹的宽和高。
4.支持IE6 7 8 9 10 firefox chrome等浏览器。

效果预览:
javascript椭圆旋转相册实现代码
实现原理:

根据对图片在椭圆轨迹上的运动,动态改变缩放大小,实现立体的视觉效果。
代码分析:

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>
Javascript 相关文章推荐
javascript正则表达式中参数g(全局)的作用
Nov 11 Javascript
javascript版的in_array函数(判断数组中是否存在特定值)
May 09 Javascript
JavaScript实现选择框按比例拖拉缩放的方法
Aug 04 Javascript
javascript中undefined与null的区别
Aug 16 Javascript
JavaScript Length 属性的总结
Nov 02 Javascript
轻量级jQuery插件slideBox实现带底栏轮播(焦点图)代码
Mar 28 Javascript
支持移动端原生js轮播图
Feb 16 Javascript
Vue.js手风琴菜单组件开发实例
May 16 Javascript
vue keep-alive请求数据的方法示例
May 16 Javascript
详解js模板引擎art template数组渲染的方法
Oct 09 Javascript
vue.js中使用微信扫一扫解决invalid signature问题(完美解决)
Apr 11 Javascript
Echarts在Taro微信小程序开发中的踩坑记录
Nov 09 Javascript
Prototype源码浅析 Number部分
Jan 16 #Javascript
Prototype源码浅析 String部分(四)之补充
Jan 16 #Javascript
Prototype源码浅析 String部分(二)
Jan 16 #Javascript
深入理解JavaScript系列(11) 执行上下文(Execution Contexts)
Jan 15 #Javascript
深入理解JavaScript系列(10) JavaScript核心(晋级高手必读篇)
Jan 15 #Javascript
深入理解JavaScript系列(9) 根本没有“JSON对象”这回事!
Jan 15 #Javascript
深入理解JavaScript系列(8) S.O.L.I.D五大原则之里氏替换原则LSP
Jan 15 #Javascript
You might like
php 显示指定路径下的图片
2009/10/29 PHP
PHP 反向排序和随机排序代码
2010/06/30 PHP
那些年一起学习的PHP(二)
2012/03/21 PHP
thinkphp,onethink和thinkox中验证码不显示的解决方法分析
2016/06/06 PHP
php递归函数怎么用才有效
2018/02/24 PHP
网上抓的一个特效
2007/05/11 Javascript
JavaScript 高级语法介绍
2009/06/15 Javascript
基于jquery的direction图片渐变动画效果
2010/05/24 Javascript
ExtJS中文乱码之GBK格式编码解决方案及代码
2013/01/20 Javascript
EditPlus中的正则表达式 实战(2)
2016/12/15 Javascript
jQuery插件ContextMenu自定义图标
2017/03/15 Javascript
详谈angularjs中路由页面强制更新的问题
2017/04/24 Javascript
vue2.0.js的多级联动选择器实现方法
2018/02/09 Javascript
快速解决vue动态绑定多个class的官方实例语法无效的问题
2018/09/05 Javascript
快速搭建Node.js(Express)用户注册、登录以及授权的方法
2019/05/09 Javascript
解决Vue.js应用回退或刷新界面时提示用户保存修改问题
2019/11/24 Javascript
微信小程序实现单个或多个倒计时功能
2020/11/01 Javascript
vue中watch的用法汇总
2020/12/28 Vue.js
[06:10]6.81新信使新套装!给你一个炫酷的DOTA2
2014/05/06 DOTA
[01:31:02]TNC vs VG 2019国际邀请赛淘汰赛 胜者组赛BO3 第一场
2019/08/22 DOTA
Python3安装Scrapy的方法步骤
2017/11/23 Python
python删除字符串中指定字符的方法
2018/08/13 Python
Django数据库连接丢失问题的解决方法
2018/12/29 Python
python并发编程多进程 互斥锁原理解析
2019/08/20 Python
python 定义类时,实现内部方法的互相调用
2019/12/25 Python
Laura Mercier官网:彩妆大师罗拉玛斯亚的化妆品牌
2018/01/04 全球购物
Zavvi荷兰:英国大型音像制品和图书游戏零售商
2018/03/22 全球购物
日本最大级玩偶手办购物:あみあみ Amiami
2018/04/23 全球购物
中学生获奖感言
2014/02/04 职场文书
销售员岗位职责
2015/02/10 职场文书
消费者投诉书范文
2015/07/02 职场文书
2016保送生自荐信范文
2016/01/29 职场文书
SpringBoot连接MySQL获取数据写后端接口的操作方法
2021/11/02 MySQL
基于Pygame实现简单的贪吃蛇游戏
2021/12/06 Python
Redis 限流器
2022/05/15 Redis
Redis特殊数据类型bitmap位图
2022/06/01 Redis