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 相关文章推荐
jQuery getJSON()+.ashx 实现分页(改进版)
Mar 28 Javascript
原生javaScript实现图片延时加载的方法
Dec 22 Javascript
JavaScript中setUTCMilliseconds()方法的使用详解
Jun 12 Javascript
js字符串截取函数slice、substring和substr的比较
May 17 Javascript
基于jQuery实现淡入淡出效果轮播图
Jul 31 Javascript
ajax异步请求详解
Jan 06 Javascript
Javascript中this关键字指向问题的测试与详解
Aug 11 Javascript
jQuery替换节点元素的操作方法
Mar 18 jQuery
微信小程序项目实践之九宫格实现及item跳转功能
Jul 19 Javascript
基于JavaScript实现一个简单的Vue
Sep 26 Javascript
基于vue 动态菜单 刷新空白问题的解决
Aug 06 Javascript
JQuery通过键盘控制键盘按下与松开触发事件
Aug 07 jQuery
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
使用YUI+Ant 实现JS CSS压缩
2014/09/02 PHP
CakePHP框架Model关联对象用法分析
2017/08/04 PHP
laravel实现批量更新多条记录的方法示例
2017/10/22 PHP
PHP 图片处理
2020/09/16 PHP
jquery 如何动态添加、删除class样式方法介绍
2012/11/07 Javascript
JS实现切换标签页效果实例代码
2013/11/01 Javascript
jquery遍历数组与筛选数组的方法
2013/11/05 Javascript
jQuery过滤选择器:not()方法使用介绍
2014/04/20 Javascript
常见的jQuery选择器汇总
2014/11/24 Javascript
javascript截取字符串小结
2015/04/28 Javascript
js实现旋转木马效果
2017/03/17 Javascript
Vue 项目部署到服务器的问题解决方法
2017/12/05 Javascript
JS简单添加元素新节点的方法示例
2018/02/10 Javascript
js装饰设计模式学习心得
2018/02/17 Javascript
React-router4路由监听的实现
2018/08/07 Javascript
Bootstrap 按钮样式与使用代码详解
2018/12/09 Javascript
基于elementUI实现图片预览组件的示例代码
2019/03/31 Javascript
搭建一个Koa后端项目脚手架的方法步骤
2019/05/30 Javascript
Vue之beforeEach非登录不能访问的实现(代码亲测)
2019/07/18 Javascript
vue通过过滤器实现数据格式化
2020/07/20 Javascript
Python获取linux主机ip的简单实现方法
2016/04/18 Python
Python常见异常分类与处理方法
2017/06/04 Python
Python求一批字符串的最长公共前缀算法示例
2019/03/02 Python
python程序运行进程、使用时间、剩余时间显示功能的实现代码
2019/07/11 Python
python更新数据库中某个字段的数据(方法详解)
2020/11/18 Python
python代码实现猜拳小游戏
2020/11/30 Python
英国领先的票务代理商之一:The Ticket Factory
2019/02/09 全球购物
Jowissa官方网站:瑞士制造的手表,优雅简约的设计
2020/07/29 全球购物
管理学专业个人求职信范文
2013/09/21 职场文书
QA工程师岗位职责
2013/11/20 职场文书
家长学校培训材料
2014/08/20 职场文书
群众路线组织生活会发言材料
2014/10/17 职场文书
体育教师个人工作总结
2015/02/09 职场文书
2016猴年春节慰问信
2015/11/30 职场文书
nginx中封禁ip和允许内网ip访问的实现示例
2022/03/17 Servers
Mysql查询时间区间日期列表,不会由于数据表数据影响
2022/04/19 MySQL