<div class="carousel-box"> <div class="carousel"> <ul class="clearfix" > <li><img src="img/carousel01.jpg" alt=""></li> <li><img src="img/carousel02.jpg" alt=""></li> <li><img src="img/carousel03.jpg" alt=""></li> <li><img src="img/carousel04.jpg" alt=""></li> <li><img src="img/carousel05.jpg" alt=""></li> <li><img src="img/carousel06.jpg" alt=""></li> </ul> </div> </div>
/*須要更改的值*/ .carousel img{ width: 600px; height: 400px; } .carousel, .carousel-box { width: 600px; /*單張圖片寬度*/ height: 400px; /*單張圖片高度*/ } .carousel ul{ width: 3600px; /*單張圖片寬度x圖片數量*/ }
將全部圖片橫向排列,最外層容器和包裹容器設置overflow:hidden。最外層容器用於按鈕和箭頭的定位。利用包裹容器的scrollLeft屬性控制顯示哪張圖片。css
想要實現這些功能,應該有如下一些方法:html
1.圖片切換函數。接受一個參數,表示滾動方向。調用緩動函數切換圖片。調用切換按鈕圖標函數點亮相應的按鈕。git
2.緩動函數。github
3.點亮按鈕函數。性能優化
4.初始化函數。用於綁定事件,建立按鈕和箭頭,初始化最初位置。閉包
5.建立箭頭函數。app
6.建立按鈕函數。less
7.開始輪播函數。ide
8.輪播函數。函數
9.中止函數。用於中止輪播。
還有一些公用方法:$():選擇DOM元素。addClass(ele,"className"):給元素添加類名。removeClass(ele,"className")移除元素的類名。$.add(ele,"type",fun):給一個DOM節點綁定事件。getCSS(ele,"prop"):獲取元素相應屬性的值。$.delegateTag("selector","tagName","type",fun):事件代理。
假設有6張圖片,每張圖片寬度爲600px。按照功能的獨立性來完成:
緩動函數的做用是一點一點的改變目標元素的屬性值,直到達到目標值。使用它的元素多是水平輪播的圖片,也多是垂直輪播的圖片,也多是一個想從頁面左端到達頁面右端的小盒子。因此它應該接收四個參數(目標元素,要改變的屬性值,目標值,移動次數)。
liner=function(ele,prop,next,num){ var speed=(next-ele[prop])/num, i=0; (function(){ ele[prop]+=speed; i++; if (i<num) { setTimeout(arguments.callee,30); } })(); },
點亮按鈕本質上就是給按鈕添加一個active類,熄滅按鈕就是給按鈕移除active類。
那麼如何知道當前按鈕是哪個呢?
最簡單的方法是直接獲取,因此能夠給每一個按鈕添加一個index屬性,當須要點亮按鈕時,將要點亮的按鈕的index傳給這個函數便可。
那麼如何知道要熄滅的按鈕是哪個呢?
最簡單的方法也是直接獲取,因此能夠在做用域鏈末端添加一個變量active,記住當前亮着的按鈕,這個函數直接將他熄滅就能夠了。
light=function(index){ removeClass(active,"active"); active=$(this.wrapSelec+" "+"[index="+index+"]"); addClass(active,"active"); }
須要計算出下一個scrollLeft的值:
若是是向左移動的話,scrollLeft應該-600,若是已是0,就切換爲3000.因此是ele.scrollLeft===0?width*(len-1):ele.scrollLeft-width;
若是是向右移動的話,scrollLeft應該+600,即0——>600,600——>1200,...,3000——>0。這裏能夠像上面那樣用判斷,也能夠用一個公式next=(cur+distance)%(distance*num)。即(ele.scrollLeft+width)%(width*len)
須要得到下一個要被點亮的按鈕的index:
和計算scrollLeft的思路同樣,往左移動:index===0? len-1:index-1; 往右移動:(index+1)%len
go=function(dire){ var index=active.getAttribute("index")-0, nextIndex, nextPosition; if (dire==="next") { nextIndex=(index+1)%len; nextPosition=(ele.scrollLeft+width)%(width*len); }else{ nextIndex=index===0? len-1:index-1, nextPosition=ele.scrollLeft===0?width*len:ele.scrollLeft-width; } light(nextIndex); animate.liner(ele,"scrollLeft",nextPosition); }
其中的len(圖片總數)、width(圖片寬度)、ele(包裹容器)也會被其餘函數訪問,因此也添加到做用域鏈末端。
len=ele.getElementsByTagName("img").length
width=parseInt(getCSS(ele.getElementsByTagName("img")[0],"width");
ele=$(eleSelec),eleSelec是包裹容器的selector,好比.carousel
建立一個向左的箭頭,綁定事件處理函數,用於向左移動。建立一個向右的箭頭,綁定事件處理函數,用於向右移動。
createArrow=function(){ var prev=document.createElement("div"), next=document.createElement("div"); prev.appendChild(document.createTextNode("<")); next.appendChild(document.createTextNode(">")); prev.className="arrow prev"; next.className="arrow next"; container.appendChild(prev); container.appendChild(next); addClass(container,"hide"); $.add(next,"click",function(){ go("next"); }); $.add(prev,"click",function(){ go("prev"); }); }
container表明最外層容器,也會被其餘函數訪問,因此也添加到做用域鏈末端。
container=$(wrapSelec),wrapSelec是最外層容器的selector,好比.carousel-box
給每一個按鈕添加一個index用於點亮和熄滅,給按鈕組添加一個類名用於設置樣式和獲取它:
createBtn=function(){ var div=document.createElement("div"), btns=''; for(var i=0;i<len;i++){ btns+='<a href="#" index="'+i+'"></a>'; } div.innerHTML=btns; addClass(div,"carousel-btn"); container.appendChild(div); }
根據要求(順時針、逆時針)判斷要調用go("prev")仍是go("next")。
若是要求循環,則再次調用本身。若是不循環,則在輪播一輪後中止。
因此這裏須要一個變量來判斷方向,一個變量來判斷是否循環,一個變量來計數。
因此又有四個變量被加到做用域鏈末端。direction、loop、count、begin用於清除定時器。
circle=function(){ count++; if (loop||count<len) { if (direction==="forward") { go("next"); }else{ go("prev"); } }
begin=setTimeout(arguments.callee,t); }
7.中止函數 stop
stop=function(){ clearTimeout(begin); }
若是是第一次使用輪播,則建立按鈕和箭頭,並給按鈕綁定click事件處理程序(獲取點擊的按扭index點亮它,切換到相應圖片),而後根據順時針或逆時針來展現相應的圖片和按鈕。
因此這裏又須要有一個變量加在做用域鏈末端,用於表示是否已經初始化。
init=function(){ createBtn(); createArrow(); $.delegateTag(wrapSelec+" "+".carousel-btn","a","click",function(e,target){ $.prevent(e); light(target.getAttribute("index")); animate.liner(ele,"scrollLeft",target.getAttribute("index")*width); }); $.add(container,"mouseenter",function(){ stop(); removeClass(container,"hide"); }); $.add(container,"mouseleave",function(){ addClass(container,"hide"); begin=setTimeout(circle,t); });if (direction==="forward") { light(0); }else{ light(len-1); ele.scrollLeft=width*(len-1); } haveStart=true; }
9.開始輪播函數 start
這個函數當作接口,用於控制輪播方向,間隔時間,和是否循環。計數器歸零。
由於可能重複的開始輪播,因此每次開始以前都須要清除定時器。
start=function(dir,th,lo){ stop();
count=0; direction=dir; t=th*1000; loop=lo; if (!haveStart) { init(); } begin=setTimeout(circle,t); }
到這裏,全部須要用到的函數已經寫完了,若是把這些函數和那些須要的變量扔到一個函數裏,把外層容器盒包裹容器的類名或ID傳給它,這個函數返回一個包含start和stop方法的對象,這個組件就可使用了。
可是有一個問題,這個函數只有一個,也就是說,一個頁面只能有一個輪播實例。因此,若是想要一個頁面能有兩個輪播實例都用這個組件的話,就不能把它們扔到一個函數裏。那麼就只能放到對象裏。每一個對象有本身的變量,他們共用一組方法。
那麼,這些變量就不能直接訪問了,須要經過對象的屬性訪問,即this。
這時候就會出現問題,this是會指向調用它的那個環境,因此當那些變量在事件處理程序中,或是在定時器中被訪問的時候,就不能用this,而是要建立一個閉包。
即,在能獲取到this時,將this賦值給一個變量,而後在事件處理程序或是定時器中訪問這個變量,就會獲取到正確的對象。
以init函數爲例來改裝:
carouselProto.init=function(){ var that=this; this.createBtn(); this.createArrow(); $.delegateTag(this.wrapSelec+" "+".carousel-btn","a","click",function(e,target){ $.prevent(e); that.light(target.getAttribute("index")); animate.liner(that.ele,"scrollLeft",target.getAttribute("index")*that.width); }); $.add(this.container,"mouseenter",function(){ that.stop(); removeClass(that.container,"hide"); }); $.add(this.container,"mouseleave",function(){ addClass(that.container,"hide"); that.begin=setTimeout(function(){ that.circle(); },that.t); });if (this.direction==="forward") { this.light(0); }else{ this.light(this.len-1); this.ele.scrollLeft=this.width*(this.len-1); } this.haveStart=true; };
這樣改裝完以後,就能夠建立實例了,每一個實例都會有本身的屬性用於記錄狀態,他們都共用原型中的方法。
若是採用原型繼承的方式的話,能夠建立一個對象做爲實例的原型對象,而後建立一個函數來生產實例:
var carouselProto={}; //把上面那些方法給這個對象 carouselProto.light=... carouselProto.go=... carouselProto.stop=... //建立實例對象函數 var carousel=function(eleSelec,wrapSelec){ var that=Object.create(carouselProto); that.wrapSelec=wrapSelec; that.ele=$(eleSelec); that.container=$(wrapSelec); that.len=that.ele.getElementsByTagName("img").length; that.width=parseInt(getCSS(that.ele.getElementsByTagName("img")[0],"width")); return that; } //建立實例,使用組件 var carousel1=carousel(".carousel",".carousel-box"); carousel1.start("forward",3,true); var carousel2=carousel(".carousel2",".carousel-box2"); carousel2.start("backward",2,true);
1.當點擊的按鈕恰好是當前被點亮的按鈕時,依然會調用一次light和animate.liner。因此能夠添加一個判斷語句,若是點擊的按鈕恰好是正確的,就不要執行下面的了。
$.delegateTag(this.wrapSelec+" "+".carousel-btn","a","click",function(e,target){ $.prevent(e); var index=target.getAttribute("index"); if (index===that.active.getAttribute("index")) { return } that.light(index); animate.liner(that.ele,"scrollLeft",target.getAttribute("index")*that.width); });
2.當圖片切換的時候,緩動動畫正在執行。若是在緩動動畫還沒執行完時就點擊按鈕或者箭頭,就會進入下一次動畫,因而就會出現混亂,圖片錯位。性能也會受到影響。爲了防止這種狀況發生,可使用一個變量,用於記錄緩動動畫是否正在執行,沒有執行的話點擊按鈕或箭頭纔會執行函數。
liner=function(ele,prop,next){ var speed=(next-ele[prop])/10, i=0; ele.animating=true; (function(){ ele[prop]+=speed; i++; if (i<10) { setTimeout(arguments.callee,60); }else{ ele.animating=false; } })(); } if (!this.ele.animating) { this.light(nextIndex); animate.liner(this.ele,"scrollLeft",nextPosition); }
參考資源: