值得分享的JavaScript實(shí)現(xiàn)圖片輪播組件
本文實(shí)例為大家分享了JavaScript實(shí)現(xiàn)圖片輪播組件的使用方法,供大家參考,具體內(nèi)容如下
效果:
自動(dòng)循環(huán)播放圖片,下方有按鈕可以切換到對(duì)應(yīng)圖片。
添加一個(gè)動(dòng)畫(huà)來(lái)實(shí)現(xiàn)圖片切換。
鼠標(biāo)停在圖片上時(shí),輪播停止,出現(xiàn)左右兩個(gè)箭頭,點(diǎn)擊可以切換圖片。
鼠標(biāo)移開(kāi)圖片區(qū)域時(shí),從當(dāng)前位置繼續(xù)輪播。
提供一個(gè)接口,可以設(shè)置輪播方向,是否循環(huán),間隔時(shí)間。
點(diǎn)擊查看demo
對(duì)HTML、CSS的要求:
<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>
*必須是兩個(gè)盒子嵌套,最里面的盒子需要有一個(gè)ul,圖片需要被包含在li里。
*可以更改類名,同時(shí)將css文件中的相應(yīng)類名替換即可。配置組件時(shí)傳入正確的DOM元素即可。
*不限制圖片寬度和數(shù)量,在css文件中更改數(shù)值即可。
/*需要更改的值*/ .carousel img{ width: 600px; height: 400px; } .carousel, .carousel-box { width: 600px; /*單張圖片寬度*/ height: 400px; /*單張圖片高度*/ } .carousel ul{ width: 3600px; /*單張圖片寬度x圖片數(shù)量*/ }
原理:
將所有圖片橫向排列,最外層容器和包裹容器設(shè)置overflow:hidden。最外層容器用于按鈕和箭頭的定位。利用包裹容器的scrollLeft屬性控制顯示哪張圖片。
思路:
想要實(shí)現(xiàn)這些功能,應(yīng)該有以下一些方法:
1.圖片切換函數(shù)。接受一個(gè)參數(shù),表示滾動(dòng)方向。調(diào)用緩動(dòng)函數(shù)切換圖片。調(diào)用切換按鈕圖標(biāo)函數(shù)點(diǎn)亮相應(yīng)的按鈕。
2.緩動(dòng)函數(shù)。
3.點(diǎn)亮按鈕函數(shù)。
4.初始化函數(shù)。用于綁定事件,創(chuàng)建按鈕和箭頭,初始化最初位置。
5.創(chuàng)建箭頭函數(shù)。
6.創(chuàng)建按鈕函數(shù)。
7.開(kāi)始輪播函數(shù)。
8.輪播函數(shù)。
9.停止函數(shù)。用于停止輪播。
還有一些公用方法:
$():選擇DOM元素。
addClass(ele,"className"):給元素添加類名。
removeClass(ele,"className"):移除元素的類名。
$.add(ele,"type",fun):給一個(gè)DOM節(jié)點(diǎn)綁定事件。
getCSS(ele,"prop"):獲取元素相應(yīng)屬性的值。
$.delegateTag("selector","tagName","type",fun):事件代理。
實(shí)現(xiàn):
假設(shè)有6張圖片,每張圖片寬度為600px。按照功能的獨(dú)立性來(lái)完成:
1.緩動(dòng)函數(shù) liner
緩動(dòng)函數(shù)的作用是一點(diǎn)一點(diǎn)的改變目標(biāo)元素的屬性值,直到達(dá)到目標(biāo)值。使用它的元素可能是水平輪播的圖片,也可能是垂直輪播的圖片,也可能是一個(gè)想從頁(yè)面左端到達(dá)頁(yè)面右端的小盒子。所以它應(yīng)該接收四個(gè)參數(shù)(目標(biāo)元素,要改變的屬性值,目標(biāo)值,移動(dòng)次數(shù))。
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); } })(); },
2.點(diǎn)亮按鈕函數(shù) light
點(diǎn)亮按鈕本質(zhì)上就是給按鈕添加一個(gè)active類,熄滅按鈕就是給按鈕移除active類。
那么如何知道當(dāng)前按鈕是哪一個(gè)呢?
最簡(jiǎn)單的方法是直接獲取,所以可以給每個(gè)按鈕添加一個(gè)index屬性,當(dāng)需要點(diǎn)亮按鈕時(shí),將要點(diǎn)亮的按鈕的index傳給這個(gè)函數(shù)即可。
那么如何知道要熄滅的按鈕是哪一個(gè)呢?
最簡(jiǎn)單的方法也是直接獲取,所以可以在作用域鏈末端添加一個(gè)變量active,記住當(dāng)前亮著的按鈕,這個(gè)函數(shù)直接將他熄滅就可以了。
light=function(index){ removeClass(active,"active"); active=$(this.wrapSelec+" "+"[index="+index+"]"); addClass(active,"active"); }
3.圖片切換函數(shù) go
需要計(jì)算出下一個(gè)scrollLeft的值:
如果是向左移動(dòng)的話,scrollLeft應(yīng)該-600,如果已經(jīng)是0,就切換為3000.所以是ele.scrollLeft===0?width*(len-1):ele.scrollLeft-width;
如果是向右移動(dòng)的話,scrollLeft應(yīng)該+600,即0——>600,600——>1200,...,3000——>0。這里可以像上面那樣用判斷,也可以用一個(gè)公式next=(cur+distance)%(distance*num)。即(ele.scrollLeft+width)%(width*len)
需要獲得下一個(gè)要被點(diǎn)亮的按鈕的index:
和計(jì)算scrollLeft的思路一樣,往左移動(dòng):index===0? len-1:index-1; 往右移動(dòng):(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(圖片總數(shù))、width(圖片寬度)、ele(包裹容器)也會(huì)被其他函數(shù)訪問(wèn),所以也添加到作用域鏈末端。
len=ele.getElementsByTagName("img").length width=parseInt(getCSS(ele.getElementsByTagName("img")[0],"width");
ele=$(eleSelec),eleSelec是包裹容器的selector,比如.carousel
4.創(chuàng)建箭頭函數(shù) createArrow
創(chuàng)建一個(gè)向左的箭頭,綁定事件處理函數(shù),用于向左移動(dòng)。創(chuàng)建一個(gè)向右的箭頭,綁定事件處理函數(shù),用于向右移動(dòng)。
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代表最外層容器,也會(huì)被其他函數(shù)訪問(wèn),所以也添加到作用域鏈末端。
container=$(wrapSelec),wrapSelec是最外層容器的selector,比如.carousel-box
5.創(chuàng)建按鈕函數(shù) createBtn
給每個(gè)按鈕添加一個(gè)index用于點(diǎn)亮和熄滅,給按鈕組添加一個(gè)類名用于設(shè)置樣式和獲取它:
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); }
6.輪播函數(shù)
根據(jù)要求(順時(shí)針、逆時(shí)針)判斷要調(diào)用go("prev")還是go("next")。
如果要求循環(huán),則再次調(diào)用自己。如果不循環(huán),則在輪播一輪后停止。
所以這里需要一個(gè)變量來(lái)判斷方向,一個(gè)變量來(lái)判斷是否循環(huán),一個(gè)變量來(lái)計(jì)數(shù)。
所以又有四個(gè)變量被加到作用域鏈末端。direction、loop、count、begin用于清除定時(shí)器。
circle=function(){ count++; if (loop||count<len) { if (direction==="forward") { go("next"); }else{ go("prev"); } } begin=setTimeout(arguments.callee,t); }
7.停止函數(shù) stop
stop=function(){ clearTimeout(begin); }
8.初始化函數(shù) init
如果是第一次使用輪播,則創(chuàng)建按鈕和箭頭,并給按鈕綁定click事件處理程序(獲取點(diǎn)擊的按扭index點(diǎn)亮它,切換到相應(yīng)圖片),然后根據(jù)順時(shí)針或逆時(shí)針來(lái)展示相應(yīng)的圖片和按鈕。
所以這里又需要有一個(gè)變量加在作用域鏈末端,用于表示是否已經(jīng)初始化。
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.開(kāi)始輪播函數(shù) start
這個(gè)函數(shù)當(dāng)做接口,用于控制輪播方向,間隔時(shí)間,和是否循環(huán)。計(jì)數(shù)器歸零。
因?yàn)榭赡苤貜?fù)的開(kāi)始輪播,所以每次開(kāi)始之前都需要清除定時(shí)器。
start=function(dir,th,lo){ stop(); count=0; direction=dir; t=th*1000; loop=lo; if (!haveStart) { init(); } begin=setTimeout(circle,t); }
到這里,所有需要用到的函數(shù)已經(jīng)寫(xiě)完了,如果把這些函數(shù)和那些需要的變量扔到一個(gè)函數(shù)里,把外層容器盒包裹容器的類名或ID傳給它,這個(gè)函數(shù)返回一個(gè)包含start和stop方法的對(duì)象,這個(gè)組件就可以使用了。
但是有一個(gè)問(wèn)題,這個(gè)函數(shù)只有一個(gè),也就是說(shuō),一個(gè)頁(yè)面只能有一個(gè)輪播實(shí)例。所以,如果想要一個(gè)頁(yè)面能有兩個(gè)輪播實(shí)例都用這個(gè)組件的話,就不能把它們?nèi)拥揭粋€(gè)函數(shù)里。那么就只能放到對(duì)象里。每個(gè)對(duì)象有自己的變量,他們共用一組方法。
那么,這些變量就不能直接訪問(wèn)了,需要通過(guò)對(duì)象的屬性訪問(wèn),即this。
這時(shí)候就會(huì)出現(xiàn)問(wèn)題,this是會(huì)指向調(diào)用它的那個(gè)環(huán)境,所以當(dāng)那些變量在事件處理程序中,或是在定時(shí)器中被訪問(wèn)的時(shí)候,就不能用this,而是要?jiǎng)?chuàng)建一個(gè)閉包。
即,在能獲取到this時(shí),將this賦值給一個(gè)變量,然后在事件處理程序或是定時(shí)器中訪問(wèn)這個(gè)變量,就會(huì)獲取到正確的對(duì)象。
以init函數(shù)為例來(lái)改裝:
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; };
這樣改裝完之后,就可以創(chuàng)建實(shí)例了,每個(gè)實(shí)例都會(huì)有自己的屬性用于記錄狀態(tài),他們都共用原型中的方法。
如果采用原型繼承的方式的話,可以創(chuàng)建一個(gè)對(duì)象作為實(shí)例的原型對(duì)象,然后創(chuàng)建一個(gè)函數(shù)來(lái)生產(chǎn)實(shí)例:
var carouselProto={}; //把上面那些方法給這個(gè)對(duì)象 carouselProto.light=... carouselProto.go=... carouselProto.stop=... //創(chuàng)建實(shí)例對(duì)象函數(shù) 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; } //創(chuàng)建實(shí)例,使用組件 var carousel1=carousel(".carousel",".carousel-box"); carousel1.start("forward",3,true); var carousel2=carousel(".carousel2",".carousel-box2"); carousel2.start("backward",2,true);
性能優(yōu)化:
1.當(dāng)點(diǎn)擊的按鈕剛好是當(dāng)前被點(diǎn)亮的按鈕時(shí),依然會(huì)調(diào)用一次light和animate.liner。所以可以添加一個(gè)判斷語(yǔ)句,如果點(diǎn)擊的按鈕剛好是正確的,就不要執(zhí)行下面的了。
$.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.當(dāng)圖片切換的時(shí)候,緩動(dòng)動(dòng)畫(huà)正在執(zhí)行。如果在緩動(dòng)動(dòng)畫(huà)還沒(méi)執(zhí)行完時(shí)就點(diǎn)擊按鈕或者箭頭,就會(huì)進(jìn)入下一次動(dòng)畫(huà),于是就會(huì)出現(xiàn)混亂,圖片錯(cuò)位。性能也會(huì)受到影響。為了防止這種情況發(fā)生,可以使用一個(gè)變量,用于記錄緩動(dòng)動(dòng)畫(huà)是否正在執(zhí)行,沒(méi)有執(zhí)行的話點(diǎn)擊按鈕或箭頭才會(huì)執(zhí)行函數(shù)。
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); }
點(diǎn)擊查看源碼
參考資源:
慕課網(wǎng)——焦點(diǎn)圖輪播特效
《JavaScript:The Good Parts》
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
用JavaScript實(shí)現(xiàn)UrlEncode和UrlDecode的腳本代碼
用js自定義函數(shù)寫(xiě)的實(shí)現(xiàn)url加密解密的實(shí)現(xiàn)代碼,需要的朋友可以參考下2008-07-07JavaScript頁(yè)面刷新與彈出窗口問(wèn)題的解決方法
解決JavaScript頁(yè)面刷新與彈出窗口問(wèn)題2010-03-03javascript實(shí)現(xiàn)移動(dòng)端上的觸屏拖拽功能
這篇文章主要為大家詳細(xì)介紹了基于javascript實(shí)現(xiàn)移動(dòng)端上的觸屏拖拽功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-03-03用JavaScript獲取頁(yè)面文檔內(nèi)容的實(shí)現(xiàn)代碼
下面小編就為大家?guī)?lái)一篇用JavaScript獲取頁(yè)面文檔內(nèi)容的實(shí)現(xiàn)代碼。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-06-06JavaScript數(shù)組排序reverse()和sort()方法詳解
這篇文章主要介紹了JavaScript數(shù)組排序reverse()和sort()方法詳解,需要的朋友可以參考下2017-12-12基于JS實(shí)現(xiàn)帶并發(fā)限制的異步調(diào)度器
這篇文章主要為大家詳細(xì)介紹了如何基于JS實(shí)現(xiàn)帶并發(fā)限制的異步調(diào)度器,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,需要的可以參考下2023-05-05mvvm雙向綁定機(jī)制的原理和實(shí)現(xiàn)代碼(推薦)
下面小編就為大家?guī)?lái)一篇mvvm雙向綁定機(jī)制的原理和實(shí)現(xiàn)代碼(推薦)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-06-06