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初学困境—js初学
Dec 29 Javascript
js左侧多级菜单动态的解决方案
Feb 01 Javascript
添加JavaScript重载函数的辅助方法2
Jul 04 Javascript
用按钮控制iframe显示的网页实现方法
Feb 04 Javascript
JQuery入门——移除绑定事件unbind方法概述及应用
Feb 05 Javascript
单击浏览器右上角的X关闭窗口弹出提示的小例子
Jun 12 Javascript
Javascript BOM学习小结(六)
Nov 26 Javascript
如何使用bootstrap框架 bootstrap入门必看!
Apr 13 Javascript
vue2.0中vue-cli实现全选、单选计算总价格的实例代码
Jul 18 Javascript
mockjs,json-server一起搭建前端通用的数据模拟框架教程
Dec 18 Javascript
vue之父子组件间通信实例讲解(props、$ref、$emit)
May 22 Javascript
Vue路由对象属性 .meta $route.matched详解
Nov 04 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 socke 向指定页面提交数据
2008/07/23 PHP
php empty函数判断mysql表单是否为空
2010/04/12 PHP
php设计模式之委托模式
2016/02/13 PHP
php脚本守护进程原理与实现方法详解
2017/07/20 PHP
laravel框架数据库配置及操作数据库示例
2019/10/10 PHP
在TP5数据库中四个字段实现无限分类的示例
2019/10/18 PHP
PHP的图像处理实例小结【文字水印、图片水印、压缩图像等】
2019/12/20 PHP
tagName的使用,留一笔
2006/06/26 Javascript
javaScript 读取和设置文档元素的样式属性
2009/04/14 Javascript
网站页面自动跳转实现方法PHP、JSP(上)
2010/08/01 Javascript
JavaScript返回上一页的三种方法及区别介绍
2015/07/04 Javascript
基于javascript实现窗口抖动效果
2016/01/03 Javascript
Angular中使用ui router实现系统权限控制及开发遇到问题
2016/09/23 Javascript
JavaScript实现的鼠标响应颜色渐变效果完整实例
2017/02/18 Javascript
详解node.js搭建代理服务器请求数据
2017/04/08 Javascript
jquery自定义显示消息数量
2017/12/19 jQuery
浅谈Express.js解析Post数据类型的正确姿势
2019/05/30 Javascript
微信小程序后台持续定位功能使用详解
2019/08/23 Javascript
vue 调用 RESTful风格接口操作
2020/08/11 Javascript
[03:16]DOTA2完美大师赛主赛事首日集锦
2017/11/23 DOTA
python with statement 进行文件操作指南
2014/08/22 Python
python安装cx_Oracle模块常见问题与解决方法
2017/02/21 Python
Python实现的银行系统模拟程序完整案例
2019/04/12 Python
pytorch 获取tensor维度信息示例
2020/01/03 Python
Django自关联实现多级联动查询实例
2020/05/19 Python
python爬虫工具例举说明
2020/11/30 Python
关于多种方式完美解决Python pip命令下载第三方库的问题
2020/12/21 Python
世界上最大的家庭自动化公司:Smarthome
2017/12/20 全球购物
亚马逊海外购:亚马逊美国、英国、日本、德国直邮
2021/03/18 全球购物
校领导推荐信
2013/11/01 职场文书
纪念九一八事变演讲稿:牢记九一八,屈辱怎能忘
2014/09/14 职场文书
2014年幼儿园学期工作总结
2014/12/05 职场文书
初中英语教师个人工作总结2015
2015/07/21 职场文书
Java输出Hello World完美过程解析
2021/06/13 Java/Android
Nginx反向代理至go-fastdfs案例讲解
2021/08/02 Servers
CSS中使用grid布局实现一套模板多种布局
2022/07/15 HTML / CSS