原生JS實(shí)現(xiàn)旋轉(zhuǎn)木馬式圖片輪播插件
本人自己寫過三個(gè)圖片輪播,一個(gè)是簡(jiǎn)單的原生JS實(shí)現(xiàn)的,沒有什么動(dòng)畫效果的,一個(gè)是結(jié)合JQuery實(shí)現(xiàn)的,淡入淡出切換的?,F(xiàn)在想做一個(gè)酷一點(diǎn)的放在博客或者個(gè)人網(wǎng)站,到時(shí)候可以展示自己的作品。逛了一下慕課網(wǎng),發(fā)現(xiàn)有個(gè)旋轉(zhuǎn)木馬的jquery插件課程,有點(diǎn)酷酷的,于是就想著用原生JS封裝出來。做起來才發(fā)現(xiàn),沒有自己想象中的那么容易。。。不啰嗦了,講解一下實(shí)現(xiàn)過程吧。
效果
由于自己的服務(wù)器還沒弄好。在線演示不了(ORZ...),只能放一張效果圖了。
從圖片上還是可以看出大概效果的,我就不多說了。想看真實(shí)代碼效果的,歡迎到我的github上面download代碼,別忘了給我的github項(xiàng)目點(diǎn)個(gè)星星噢^_^
實(shí)現(xiàn)過程
html結(jié)構(gòu)
<div class="carrousel-main" data-setting='{"width":1000,"height":400, "carrouselWidth":750, "carrouselHeight":400, "scale":0.9, "verticalAlign":"middle"}'> <div class="carrousel-btn carrousel-btn-pre"></div> <ul class="carrousel-list"> <li class="carrousel-item"> <a href="#"><img src="img/1.jpg"></a> </li> <li class="carrousel-item"> <a href="#"><img src="img/2.jpg"></a> </li> <li class="carrousel-item"> <a href="#"><img src="img/3.jpg"></a> </li> <li class="carrousel-item"> <a href="#"><img src="img/4.jpg"></a> </li> <li class="carrousel-item"> <a href="#"><img src="img/5.jpg"></a> </ul> <div class="carrousel-btn carrousel-btn-next"></div> </div>
這個(gè)結(jié)構(gòu)和一般輪播的html代碼結(jié)構(gòu)是一樣的,稍有不同就是,主輪播div上面有一個(gè)data-setting的屬性,這個(gè)屬性里面就是一些輪播效果的參數(shù)。參數(shù)的具體意義稍后再講解。
css部分的代碼就不貼了,最重要就是要注意元素的絕對(duì)定位,由效果圖可以看出來,每張圖片的位置、大小都不一樣,所以這些都是通過js控制的,因此需要絕對(duì)定位。下面重點(diǎn)講一下js實(shí)現(xiàn)過程。
JS實(shí)現(xiàn)過程
下面講幾個(gè)JS的關(guān)鍵步驟,做好了這幾個(gè)步驟之后,應(yīng)該就沒有什么難點(diǎn)了。
①默認(rèn)參數(shù)
既然是封裝插件,那么肯定會(huì)有一些參數(shù)的默認(rèn)值需要配置的啦。這個(gè)插件中,主要有如下參數(shù):
width:1000, //幻燈片區(qū)域的寬度
height:400, //幻燈片區(qū)域的高度
carrouselWidth:700, //幻燈片第一幀的寬度
carrouselHeight:400, //幻燈片第一幀的高度
scale:0.9,//記錄顯示比例關(guān)系,例如第二張圖比第一張圖顯示的時(shí)候?qū)捀咝《嗌?br />
autoPlay:true,//是否自動(dòng)播放
timeSpan:3000,//自動(dòng)播放時(shí)間間隔
verticalAlign:'middle' //圖片對(duì)齊方式,有top\middle\bottom三種方式,默認(rèn)為middle
②封裝對(duì)象
因?yàn)橐粋€(gè)網(wǎng)站可能有多個(gè)地方都會(huì)用到同一個(gè)輪播插件,所以封裝很關(guān)鍵。定義了這個(gè)對(duì)象之后,如果給對(duì)象定義一個(gè)初始化方法是可以創(chuàng)建多個(gè)對(duì)象的,只需要把所有類相同的dom傳進(jìn)去就可以了。所以,我的初始化方法如下:
Carousel.init=function(carrousels){ var _this=this; //將nodeList轉(zhuǎn)換為數(shù)組 var cals= toArray(carrousels); <br> /*因?yàn)樵鶭S獲取所有的類,得到的是一個(gè)nodeList,是一個(gè)類數(shù)組,如果想要使用數(shù)組的方法則需要轉(zhuǎn)化為真正的數(shù)組。這里toArray為轉(zhuǎn)化方法。*/ cals.forEach(function(item,index,array){ new _this(item); }); }
這樣的話,我在window.onload的時(shí)候,調(diào)用Carrousel.init(document.querySelectorAll('.carrousel-main'));這樣就可以創(chuàng)建多個(gè)輪播啦!
③初始化好第一幀的位置參數(shù)
因?yàn)?,第一幀之后的所有幀的相關(guān)參數(shù)都是參照第一幀來定義的,因此,定義好第一幀很關(guān)鍵。
setValue:function(){ this.carrousel.style.width=this.Settings.width+'px'; this.carrousel.style.height=this.Settings.height+'px'; /*左右按鈕設(shè)置,這里要讓左右按鈕平均地瓜分輪播區(qū)域?qū)挏p去第一幀寬度之后的區(qū)域,z-index要比除第一幀外所有圖片都高,而圖片剛好左右分放置,因此z-index的值就是圖片數(shù)量的一半。*/ var btnW=(this.Settings.width-this.Settings.carrouselWidth)/2; this.preBtn.style.width=btnW+'px'; this.preBtn.style.height=this.Settings.height+'px'; this.preBtn.style.zIndex=Math.ceil(this.carrouselItems.length/2); this.nextBtn.style.width=btnW+'px'; this.nextBtn.style.height=this.Settings.height+'px'; this.nextBtn.style.zIndex=Math.ceil(this.carrouselItems.length/2); //第一幀相關(guān)設(shè)置 this.carrouselFir.style.left=btnW+'px'; this.carrouselFir.style.top=this.setCarrouselAlign(this.Settings.carrouselHeight)+'px'; this.carrouselFir.style.width=this.Settings.carrouselWidth+'px'; this.carrouselFir.style.height=this.Settings.carrouselHeight+'px'; this.carrouselFir.style.zIndex=Math.floor(this.carrouselItems.length/2); },
這里,就是new對(duì)象的時(shí)候,就到dom結(jié)點(diǎn)中獲取data-setting參數(shù),然后更新默認(rèn)參數(shù)配置。這里有個(gè)地方需要注意的,獲取dom的參數(shù)不能直接以賦值的方式更新默認(rèn)參數(shù),因?yàn)橛脩襞渲脜?shù)的時(shí)候,不一定會(huì)所有參數(shù)都配置一次。如果直接賦值而用戶剛好不是所有參數(shù)都配置的話就會(huì)造成參數(shù)丟失。這里我是自己寫了一個(gè)類似JQuery中的$.extend方法的對(duì)象擴(kuò)展方法來更新參數(shù)的。具體就不列舉了,感興趣的可以去下載。
④其他圖片位置設(shè)置
這里的圖片實(shí)際上就是把除第一張之外的圖片,平均地分到左右兩則,而左邊的圖片位置和右邊的是不同的,因此需要分別配置:
//設(shè)置右邊圖片的位置關(guān)系 var rightIndex=level; rightSlice.forEach(function(item,index,array){ rightIndex--; var i=index; rw=rw*carrouselSelf.Settings.scale;//右邊的圖片是按照scale比例逐漸變小的 rh=rh*carrouselSelf.Settings.scale; item.style.zIndex=rightIndex;//越往右邊z-index的值越小,可以用圖片數(shù)量的一般逐漸遞減 item.style.width=rw+'px'; item.style.height=rh+'px'; item.style.opacity=1/(++i);//越往右邊透明度越小<br> //這里的gap計(jì)算方法為:輪播區(qū)域減去第一幀寬度,除2,再除左邊或者右邊的圖片張數(shù) item.style.left=(constOffset+(++index)*gap-rw)+'px';//left的值實(shí)際上就是第一幀的left+第一幀的寬度+item的間距減去item的寬度 item.style.top=carrouselSelf.setCarrouselAlign(rh)+'px'; });
左邊的設(shè)置方法類似且更為簡(jiǎn)單,就不細(xì)說了。
⑤旋轉(zhuǎn)時(shí)所有圖片的位置大小調(diào)整
這一步很關(guān)鍵,點(diǎn)擊右邊按鈕下一張的即為左旋轉(zhuǎn),而點(diǎn)擊左邊按鈕上一張即為右旋轉(zhuǎn)。此時(shí),我們只需要把所有的圖片看成一個(gè)環(huán)形那樣,點(diǎn)擊一次,換一次位置即完成旋轉(zhuǎn)。具體為左旋轉(zhuǎn)的時(shí)候,令第二張的參數(shù)等于第一張,第三張等于第二張...而最后一張等于第一張即可。右旋轉(zhuǎn)的時(shí)候,令第一張的參數(shù)等于第二張,第二張的參數(shù)等于第三張...而最后一張的參數(shù)等于第一張即可。
這里就說說左旋轉(zhuǎn)吧
if(dir=='left'){ toArray(this.carrouselItems).forEach(function(item,index,array){ var pre; if(index==0){//判斷是否為第一張 pre=_this.carrouselLat;//讓第一張的pre等于最后一張 var width=pre.offsetWidth; //獲取相應(yīng)參數(shù) var height=pre.offsetHeight; var zIndex=pre.style.zIndex; var opa=pre.style.opacity; var top=pre.style.top; var left=pre.style.left; }else{ var width = tempWidth; var height = tempHeight; var zIndex = tempZIndex; var opa = tempOpacity; var top = tempTop; var left = tempLeft; } //這里需要注意,因?yàn)榈诙垐D片是參照第一張的,而這樣改變的時(shí)候,第一張是首先被改變的,因此必須先把第一張的相關(guān)參數(shù)臨時(shí)保存起來。 tempWidth = item.offsetWidth; tempHeight = item.offsetHeight; tempZIndex = item.style.zIndex; tempOpacity = item.style.opacity; tempTop = item.style.top; tempLeft = item.style.left; item.style.width=width+'px'; item.style.height=height+'px'; item.style.zIndex=zIndex; item.style.opacity=opa; item.style.top=top; // item.style.left=left; animate(item,'left',left,function(){//自定義的原生js動(dòng)畫函數(shù) _this.rotateFlag=true; }); }); }
這里的旋轉(zhuǎn),如果不使用一些動(dòng)畫過度,會(huì)顯得很生硬。但是原生JS并沒有動(dòng)畫函數(shù),這里我是自己寫了一個(gè)模仿的動(dòng)畫函數(shù)。其原理就是獲取dom原來的樣式值,與新傳入的值比較。用一些方法定義一個(gè)速度。我這里的速度就是用其差值除18.然定義一個(gè)計(jì)時(shí)器,參考了一下jquery源碼里面的時(shí)間間隔為每13毫秒執(zhí)行一次。然后才原來的樣式值每次加上speed后等于傳入的值的時(shí)候清楚計(jì)時(shí)器即可。具體可以看這里。
好啦,關(guān)鍵的地方都差不多啦,如果明白這些過程應(yīng)該就很容易了!
四、總結(jié)思考
總結(jié):
個(gè)人感覺這還是一個(gè)比較好理解的插件。如果能結(jié)合JQuery來做就相當(dāng)簡(jiǎn)單了。但是用原生來寫的話,還是有一些不那么流暢的感覺。應(yīng)該是自定義動(dòng)畫比不上JQuery封裝好的動(dòng)畫吧。
還有,這個(gè)插件因?yàn)閳D片需要平均分到左右兩邊,于是對(duì)于偶數(shù)數(shù)量的圖片來說,這里用的方法是克隆第一張,然后加到最后,形成一個(gè)奇數(shù)。
思考:
如果說有bug的話,那就是我定義了一個(gè)rotateFlag的標(biāo)志去判斷當(dāng)前能否旋轉(zhuǎn),就是預(yù)防快速點(diǎn)擊的時(shí)候跟不上。我在按下的時(shí)候把rotateFlag設(shè)置為false,然后在動(dòng)畫結(jié)束后再把rotateFlag設(shè)置為true,但是好像作用并不明顯,希望有關(guān)大神可以指教一下,大家共同進(jìn)步。
以上就是本文的全部?jī)?nèi)容,更多內(nèi)容請(qǐng)參考:javascript圖片輪播效果匯總 ,謝謝大家的閱讀。
相關(guān)文章
JavaScript實(shí)現(xiàn)可拖動(dòng)模態(tài)框
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)可拖動(dòng)模態(tài)框,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10JS常見問題之為什么點(diǎn)擊彈出的i總是最后一個(gè)
最近有很多朋友問我,為什么點(diǎn)擊彈出的i總是最后一個(gè),于是抽時(shí)間寫了這篇文章,特此分享到腳本之家平臺(tái),供大家參考2016-01-01JavaScript實(shí)現(xiàn)多文件下載方法解析
這篇文章主要介紹了JavaScript實(shí)現(xiàn)多文件下載方法解析,文章通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08