【CSON原創】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方法添加自己的緩動模式。

  更多關於緩動的話題可以參考這兩篇文章:

/*    添加緩動模式    */
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();};

 

歡迎轉載,請標明出處:http://www.cnblogs.com/Cson/archive/2012/01/15/2322798.html#



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM