原生JavaScript實現無縫輪播圖
無縫輪播圖是頁面常用的特效之一,然而在實際的開發過程中,大部分的開發者都會使用插件來對輪播圖進行開發,那麽它的底層到底是怎麽實現的呢,本文章將圍繞這一問題展開探討。
在討論如何利用原生JS實現圖片之間無縫切換輪播的動畫效果之前,我們先來談談無縫輪播圖片的css布局。
首先我們需要一個用來顯示圖片的DIV容器,然後把想要輪播的圖片沒有縫隙的排成一行放入DIV容器中,給DIV容器設置 overflow: hidden,這樣在頁面中就可以只看到一張圖片,然後通過利用JS來移動ul的left值就能達到無縫輪播的動畫效果。
然而這還不夠,我們還需要在第一張圖片前放最後一張圖片,以及在最後一張圖片後放第一張圖片,這樣做得目的是為了實現第一張圖片和最後一張圖片切換時能達到無縫的動畫效果。核心代碼和布局效果如下:
<div id="box"> <ul> <li><img src="img/5.jpg"></li> <li><img src="img/1.jpg"></li> <li><img src="img/2.jpg"></li> <li><img src="img/3.jpg"></li> <li><img src="img/4.jpg"></li> <li><img src="img/5.jpg"></li> <li><img src="img/1.jpg"></li> </ul> </div>
下圖是給DIV容器設置overflow: hidden前的效果:
布局搞定之後,接下來就是如何利用原生JS實現無縫輪播。
首先我們需要兩個核心的函數,一個用於實現圖片無縫切換時的減速運動,另一個用於獲取ul的left值(註意:無縫輪播每次移動的都是整個ul的left值)
function fnMove(ele, obj,callback) { //參數一:需要動態變化樣式的元素//參數二:一個對象 其 鍵為需要變化的css屬性名,值為css屬性目標值 例{fontSize:500,height:140,opacity:50} //參數三:回調如果動畫結束 調用該函數 clearInterval(ele.timer); //創建計時器動態的修改 目標元素的css屬性值 // 用ele存儲timer 目的不讓計時器number被釋放,導致無法阻止計時器 ele.timer = setInterval(function () { var fnStop = true; //標記 判斷是否所有動畫都達到目標值 for (var attr in obj) { //遍歷對象 獲取到所有需要修改的屬性名和目標值 var curr = 0; // 元素當前css屬性值 如果屬性是opacity 0 - 1 // 因為我們的速度大於等於1 直接操作0-1小數不太好操作 // 我們對他進行放大100倍操作 if(attr == ‘opacity‘){ curr = parseInt(getStyle(ele, attr)*100) }else { curr = parseInt(getStyle(ele, attr)) } //定義一個速度 讓目標元素 每30毫秒增加5像素 直到達到目標值停止計時器(動畫結束) //減速運動 目標值(不變) - 減去當前值(越來越接近目標) 減出來的結果越來越小 除以6不要讓當前值一瞬間達到目標值 var speed = (obj[attr] - curr)/6; //放大動作 0.3 => 1 -0.3 => -1 避免讓速度等於0 speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed); if(speed != 0){ fnStop = false; } // console.log(curr); //動態改變css屬性值 如果是透明度 我們之前給他乘以100倍 這裏需要還原回0-1的數 if(attr == ‘opacity‘){ ele.style[attr] = parseFloat((curr + speed)/100); //透明度兼容寫法 老版本瀏覽器透明度為0-100 ele.style.filter = ‘alpha(opacity:‘+(curr + speed)+‘)‘ }else { ele.style[attr] = curr + speed + ‘px‘ } } //所有動畫完成,調用回調函數(可選) if (fnStop){ clearInterval(ele.timer); if(callback){ callback(); } } }, 30)
function getStyle(ele, sAttr) { //參數一:需要獲取的元素對象 //參數二:需要獲取的樣式屬性 //兼容寫法獲取 元素css樣式的值 //高版本瀏覽器getComputedStyle(元素,偽類),不是偽類設為null,返回的是一個CSS樣式聲明對象 if (window.getComputedStyle) { sAttr = window.getComputedStyle(ele, null)[sAttr]; } else { //低版本瀏覽器 sAttr = ele.currentStyle(sAttr); } return sAttr }
有了這兩個方法,實現無縫輪播將變得簡單。
首先我們需要一個定時器,讓圖片每兩秒進行一次輪播。其次,我們還需要一個用於移動圖片的函數,把該函數放入定時器中,每兩秒調用一次,實現輪播效果。
//通過js獲取圖片/li的寬度,得到每次輪播移動多少像素 var imgWidth = liList[0].offsetWidth; //定義當前輪播圖的下標(用來記錄下一次該展示第幾張圖片) var index = 1; // 圖片從第一張開始 index==0 其實是最後一張圖片 //頁面小圓點高亮的下標 var num = 0; //開始進行輪播 box.timer = setInterval(showRight, 2000);
function showRight() { index++; num++; if (num >= spans.length) { //spans為與展示圖片數量相同的小點數組 num = 0; } if (index >= liList.length) { //上一次已經展示了第一張(li最後一張)了,該展示第二張 // 直接設置css屬性left 是沒有動畫效果的 無縫的讓最後一張跳回 第1張 ul.style.left = -imgWidth + ‘px‘; //改變index值 動畫效果的讓第一張移動到第二張 index = 2; } fnMove(ul, {left: -imgWidth * index}) activeSpan(num); //點亮與圖片相對應的小圓點 }
//高亮小圓點方法 function activeSpan(num) { for (var i = 0; i < spans.length; i++) { spans[i].className = ‘‘; } spans[num].className = ‘bgcolor‘ }
效果圖:
接下來給左右按鈕綁定點擊事件,右按鈕每次點擊就是調用一次showRight()函數,有了showRight()函數,showLeft()函數也很容易編寫出來(註意:每次點擊需要清除原來進行輪播的定時器):
left.onclick = function (e) { //阻止事件冒泡的兼容寫法 e = e || window.event; window.event ? e.cancelBubble = true : e.stopPropagation(); clearInterval(box.timer); showLeft(); }
right.onclick = function (e) { e = e || window.event; window.event ? e.cancelBubble = true : e.stopPropagation(); clearInterval(box.timer); showRight(); }
效果圖(實際有鼠標在對左右按鈕進行點擊):
最後利用for循環給每個小圓點綁定點擊事件,這裏涉及到JS一個特性:異步事件隊列。
在這裏簡單解釋一下:頁面加載時是從上往下地對代碼進行同步加載,當遇到需要事件觸發的回調函數時,JS會把該回調函數丟入異步事件隊列當中,然後繼續往下加載代碼。當頁面中有事件被觸發時,JS會從異步事件隊列中調用與該事件綁定的所有回調函數。
所以,在利用for循環給每個小圓點綁定點擊事件時,我們需要記錄下每個小圓點的下標,在調用與點擊小圓點事件綁定的回調函數中讀取與小圓點對應的下標。
//給小圓點添加點擊 function spansClick() { for (var i = 0; i < spans.length; i++) { //記錄每個小圓點的下標 spans[i].index=i; spans[i].onclick = function (e) { e = e || window.event; window.event ? e.cancelBubble = true : e.stopPropagation(); //讀取每個小圓點的下標 num = this.index; index = num + 1; //index永遠比num大1 activeSpan(num); fnMove(ul, {left: -imgWidth * index}) } } }
效果圖(實際有鼠標對小圓點進行點擊)
原生JavaScript實現無縫輪播圖