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

