原生JS實現輪播圖--第二章動畫實現

最近看了一組故宮的圖片,以爲很大氣,決定選用故宮的圖片用來展現,以下:javascript

樣式構成與上一節類似,這裏稍作調整,便再也不贅述。css

輪播圖的動畫效果,就是圖片按照規定的方向,不停滾動以展現每一張圖片,同時還能夠經過點擊以改變本來的展現順序。實現有一下幾點:html

  1. 不作點擊操做,圖片自行滾動(這裏咱們規定自右向左滾動);
  2. 點擊左右箭頭,實現圖片翻頁;
  3. 點擊提示圓點,顯示不一樣圖片;
  4. 功能整合。

自動循環滾動

圖片自行滾動務必就要使用到定時器setInterval或者setTimeout,使得移動函數每隔一段時間執行一次,以達到循環滾動的動畫效果。java

定時器的介紹

  • setInterval()方法會重複調用一個函數或執行一個代碼段,在每次調用之間具備固定的時間延遲,返回一個intervalID,能夠做爲參數傳給clearInterval()來取消該定時器。閉包

  • setTimeout()方法設置一個定時器,在定時器到期後會執行一個函數或一段代碼,返回值timeoutID是一個正整數,表示定時器的編號。這個值能夠傳遞給clearTimeout()來取消該定時器。ide

setInterval( )函數

let intervalID = window.setInterval(func, delay);
複製代碼
  • intervalID 是此重複操做的惟一辨識符,能夠做爲參數傳給clearInterval()
  • func 是你想要重複調用的函數。

setTimeout( )動畫

var timeoutID = scope.setTimeout(function, delay); 複製代碼
  • function是你想要在delay毫秒以後執行的函數。
  • 延遲的毫秒數 (一秒等於1000毫秒),函數的調用會在該延遲以後發生。

實例研究

首先,理解圖片的循環滾動,實際上就是屢次連續的移動,每次移動固定的距離,若干次的移動,就造成了循環效果,那麼此處咱們應該首個實現的是移動一次的效果,即移動函數--movefuncui

咱們先回顧一下各個容器之間的層級關係:spa

<!--最外層的父級容器-->
    <div class="imgScroll">
        <!--圖片展現區-->
        <div class="showContainer">
            <div class="container" style="left: 0px;">
                <img src="https://dimg02.c-ctrip.com/images/100b11000000qezw729A4_R_1600_10000_Mtg_7.jpg" alt="A cat">
                <img src="https://dimg05.c-ctrip.com/images/100u0x000000le38uA71D_R_1600_10000_Mtg_7.jpg" alt="A dog">
                <img src="https://dimg08.c-ctrip.com/images/100811000000qrlfxA3E0_R_1600_10000_Mtg_7.jpg" alt="dandelion">
            </div>
        </div>
        <!--底部提示區-->
        <div class="dots">
            <span class="dot active"></span>
            <span class="dot"></span>
            <span class="dot"></span>
        </div>
        <!--左右箭頭-->
        <div class="left-triangle triangle">
            <span>&lt;</span>
        </div>
        <div class="right-triangle triangle">
            <span>&gt;</span>
        </div>
    </div>

複製代碼

最外層div.imgScroll的是整個實現的父容器,其子元素div.showContainer是展現容器,裏層div.ontainer是滾動容器,該滾動容器相對於展現容器是絕對定位,那麼移動函數就是要讓滾動容器進行移動,即要改變滾動容器的style.left屬性。

移動函數--movefunc( )

上面咱們規定,圖片要自右向左滾動,即滾動容器每次向左移動一張圖片的寬度(記變量爲oneImgWidth)即div.container.style.left每次減少一個oneImgWidth

首先要獲取滾動容器,和它的的初始狀態。原生方法:getElementsByClassName()getElementById()均可以獲取DOM文檔中的元素,在html結構中沒有使用id進行元素表示,則使用前者,可是要注意該方法獲取獲得的是一個元素集合HTMLCollection,須經過下標的方式獲取第一個元素:

var container = document.getElementsByClassName("container")[0];    //獲取滾動容器
var oldLeftPos = parseInt(container.style.left);    //獲取滾動容器的初始狀態,並利用parseInt()方法取整
複製代碼

注意: 在上面的html結構中,你會注意到有一句行內樣式style="left: 0px;",這是爲了防止style.left取不到值而報錯。

而後獲取圖片的寬度,即爲滾動容器每次移動的步長:

var oneimgWidth = container.children[0].offsetWidth;    //這是一個整數
複製代碼

滾動容器左移:

var newLeftPos = oldLeftPos - oneimgWidth;
container.style.left = newLeftPos + "px";   //記得加上單位px
複製代碼

這樣就實現了一次移動效果--移動函數

function movefunc() {
    var container = document.getElementsByClassName("container")[0];    //獲取滾動容器
    var oneImgWidth = container.children[0].offsetWidth;    //獲取一張圖片的寬度,即每次左移的距離
    var oldLeft = parseInt(container.style.left);    //獲取滾動容器的初始的左距離,利用parseInt()方法取整
    var newLeft = oldLeft - oneImgWidth;
    container.style.left = newLeft + "px";      //改變滾動容器的左距離
  }
複製代碼

自動滾動函數--rollAuto( )

實現了移動函數,咱們開始正真的輪播圖實現的第一步動畫效果——自動循環滾動,前面已經介紹過定時器的基本使用,如今開始利用定時器和移動函數,實現循環移動造成動畫:

建立一個rollAuto()定時器函數:

function rullAuto() {
  setInterval("movefunc()", 800);   //每隔800毫秒執行一次移動函數
}
rullAuto();     //須要先執行一次定時器函數,才能夠開始定時器
複製代碼

在輪播圖中不進行任何操做時,圖片會自行滾動,可是當鼠標移動到展現區中時,滾動的圖片則中止滾動,鼠標再移出展現區,則滾動繼續,那麼在這一進一出的兩次操做中,就涉及了兩次對於定時器的不一樣操做,一次清除,一次恢復,其中,移進爲清除,移出爲恢復

根據上述定時器的介紹,清除定時器須要一個表示須要被清除定時器的編號,咱們使用一個變量timer來保存該定時器。

var timer = setInterval("movefunc()", 800);   //每隔800毫秒執行一次移動函數
clearInterval(timer);   //清除使移動函數循環執行的定時器
複製代碼

鼠標的移動會觸發鼠標事件事件,例如此處,鼠標移進會觸發onmouseover事件,鼠標移出會觸發onmouseout事件,則咱們能夠爲展現區元素綁定這兩個事件:

var showContainer = document.getElementsByClassName("showContainer")[0];     //獲取展現區元素
//綁定鼠標事件
showContainer.onmouseover = function() {
    clearInterval(timer);
};
showContainer.onmouseout = function() {
    rullAuto();   //鼠標移出,繼續執行移動函數
};
複製代碼

最後咱們將這個自行滾動函數整理一下:

function autoRullImg() {
    var showContainer = document.getElementsByClassName("showContainer")[0]; 
    var timer = null;
    function rullAuto() {
        timer =  setInterval("movefunc()", 800); 
    }
    rullAuto();
    showContainer.onmouseover = function() {
        clearInterval(timer);
    };
    showContainer.onmouseout = function() {
        rullAuto();
    };
}
複製代碼

這樣做爲一個完整的函數就拿過了使用,必定會讓你大失所望,圖片滾着滾着就不見了,containerstyle.left還在不斷減少。你會發現這不是咱們想要的結果。咱們但願的是,當最後一張圖片滾出展現區後,應該出現第一張圖,那麼此處就須要作一個極限條件的判斷。

var container = document.getElementsByClassName("container")[0];    //獲取滾動容器
var imgNum = container.children.length;     //獲取圖片數量
var oneImgWidth = container.children[0].offsetWidth;    //獲取每張圖的寬度(CSS樣式設置中,每張圖寬帶同樣)
//若是最後一張圖移出展現區,初始化滾動容器的.style.left屬性值
if (parseInt(container.style.left) <= -imgNum * oneImgWidth) {
    container.style.left = "0px";
}
複製代碼

最終整合版以下:

function autoRullImg() {
    var timer = null;
    var showContainer = document.getElementsByClassName("showContainer")[0]; 
    var container = document.getElementsByClassName("container")[0];  
    var imgNum = container.children.length;  
    var oneImgWidth = container.children[0].offsetWidth;  

    function rullAuto() {
        timer =  setInterval(function(){
            movefunc();
            if (parseInt(container.style.left) <= -imgNum * oneImgWidth) {
                container.style.left = "0px";
            }
        }, 800); 
    }
    rullAuto();
    
    showContainer.onmouseover = function() {
        clearInterval(timer);
    };
    showContainer.onmouseout = function() {
        rullAuto();
    };
}
複製代碼

點擊左右箭頭,實現圖片翻頁

點擊向左箭頭,滾動容器應該向右移動;點擊向右箭頭,滾動容器向左移動,這裏就出現了一個問題,咱們的移動函數,是將container.style.left減小一個圖片的寬度,即便得圖片向左移動,自行滾動函數和點擊向右箭頭效果與此相同。

可是點擊向左箭頭,則是朝着方向了,所以,咱們以前的移動函數movefunc就須要改動了,爲了區分兩個函數,咱們現將改進的移動函數改名爲movement(),再也不僅僅是移動圖片了,如果其餘元素的移動也就能夠重用該函數了。

可重用移動函數--movement( )

爲了知足兩個方向的移動,咱們就能夠把移動的步長做爲參數,傳給該函數,函數就能夠根據參數的正負值,進行不一樣方向的移動,以下:

function movement(offset) {
    var container = document.getElementsByClassName("container")[0]; 
    var oldLeft = parseInt(container.style.left); 
    var newLeft = oldLeft + offset;
    container.style.left = newLeft + "px";
  }
複製代碼

這樣就完成了一個可重用的移動函數,移動步長能夠經過參數控制。在此能夠得出,須要滾動容器向左移動時,就傳進一個負值,反之則爲正值。

那麼,autoRullImg()函數中就要把本來的移動函數替換爲新的移動函數,並傳一個負值進去:

movement(-oneImgWidth);
複製代碼

給左右箭頭綁定點擊事件:

一樣在此以前,須要先獲取元素:

var leftTriangle = document.getElementsByClassName("left-triangle")[0];         //左箭頭
var rightTriangle = document.getElementsByClassName("right-triangle")[0];       //右箭頭
複製代碼

在這裏點擊向右箭頭,container向左移動;點擊向左箭頭,container向右移動,給左右箭頭綁定點擊事件:

leftTriangle.onclick = function() {
    movement(oneImgWidth);
};
rightTriangle.onclick = function() {
    movement(-oneImgWidth);
};
複製代碼

一樣,在擊左右箭頭控制圖片移動的時候,也須要進行極限條件的判斷,在點擊向右箭頭的狀況中,若是已是第一張圖再點擊向右箭頭,應該出現的事最後一張圖;再點擊向左箭頭時,若是已經到了最後一張圖,再點擊向右箭頭,應該出現的是第一張圖。

function clickTriangle() {
    var container = document.getElementsByClassName("container")[0]; 
    var imgNum = container.children.length;  
    var oneImgWidth = container.children[0].offsetWidth;
    leftTriangle.onclick = function() {
        if (parseInt(container.style.left) >= 0) {
            container.style.left = -(imgNum - 1) * oneImgWidth + "px";
        } else {
            movement(oneImgWidth);     
        }
    };
    rightTriangle.onclick = function() {
        if (parseInt(container.style.left) <= -(imgNum - 1) * oneImgWidth) {
            container.style.left = "0px";
        } else {
            movement(-oneImgWidth);
        }
    };
}
複製代碼

點擊提示圓點,顯示不一樣圖片

給各提示圓點綁定點擊事件:

function clickDots(){
    var container = document.getElementsByClassName("container")[0];  
    var oneImgWidth = container.children[0].offsetWidth;
    var dots = document.getElementsByClassName("dots")[0].children;     //獲取提示圓點
    for(var i = 0; i < dots.length; i++){
        (function(n){
            dots[n].onclick = function(){
                container.style.left = -n*oneImgWidth + "px";
            }
        })(i);
    }
}
複製代碼

由於循環體中使用了循環變量i,須要使用建立匿名函數並當即執行的方式,解決閉包問題。

到此爲止,已經實現基本需求,圖片自行滾動、鼠標移動到展現區滾動中止、鼠標移出展現區滾動繼續、點擊向左箭頭出現當前展現圖的左邊的圖、點擊向右箭頭出現當前展現的圖片的右邊的圖片、點擊底部提示圓點出現相應圖片。

可是還有一個問題,每次圖片滾動,和點擊左右箭頭,底部的提示圓點並無相應高亮顯示。應該達到的效果是第一張圖顯示時,一個提示圓點應該高亮;第二張圖顯示時,第二個提示圓點要高亮...最後一張圖顯示時,最後一個提示圓點高亮。

所以應該有個記號,用來標記每一次展的示圖片。所以咱們聲明這樣一個記號叫作index,用來標識當前展現的圖片,其初始狀態爲0,即展現區顯示第一張圖。

記號的變化分析:

滾動容器div.container每向左滾動一次,index就應增長1,而當index增長到爲imgNum時,即最後一張圖剛好左移出展現區時,應該展現第一張圖片,即index要重置爲0;滾動容器div.container向右滾動時,則與此相反。

不管是自行滾動函數,仍是點擊事件函數,都會引發記號index的變化,因此咱們須要將index的聲明和初始化,放在這些全部函數的最外層,極限條件的判斷放進每個動畫函數autoRullImg()clickTriangle()clickDots()裏面:

//點擊向右箭頭
rightTriangle.onclick = function() {
    if (parseInt(container.style.left) <= -(imgNum - 1) * oneImgWidth) {
        container.style.left = "0px";
    } else {
        movement(-oneImgWidth);
    }
    index = index + 1;
    if (index > imgNum - 1) {
        index = 0;
    }
};

//點擊向左箭頭
leftTriangle.onclick = function() {
    if (parseInt(container.style.left) >= 0) {
        container.style.left = -(imgNum - 1) * oneImgWidth + "px";
    } else {
        movement(oneImgWidth);     
    }
    index = index - 1;
    if (index < 0) {
        index = imgNum - 1;
    }
};
複製代碼

自行滾動函數修改以下:

function autoRullImg() {
    var timer = null;
    var showContainer = document.getElementsByClassName("showContainer")[0]; 
    var container = document.getElementsByClassName("container")[0];  
    var imgNum = container.children.length;  
    var oneImgWidth = container.children[0].offsetWidth;  

    function rullAuto() {
        timer =  setInterval(function(){
            index++;
            if (index > imgNum - 1) {
                index = 0;
            }
            movement(-oneImgWidth);
            if (parseInt(container.style.left) <= -imgNum * oneImgWidth) {
                container.style.left = "0px";
            }
        }, 800); 
        if (parseInt(container.style.left) <= -imgNum * oneImgWidth) {
            container.style.left = "0px";
        }
    }
    rullAuto();
    
    showContainer.onmouseover = function() {
        clearInterval(timer);
    };
    showContainer.onmouseout = function() {
        rullAuto();
    };
}
複製代碼

高亮函數--showDots( )

有了記號,則須要一個高亮顯示的函數,根據記號index來讓底部提示圓點高亮顯示,咱們先獲取包含提示圓點的圓點容器,而後獲取他的子元素,即<span class="dot"></span>小圓點,遍歷這些元素,對比它們的下標,若是和記號index相同,則改變該提示圓點爲高亮,即添加一個active類。

function showDots(target) {
        var dots = document.getElementsByClassName("dots")[0].children;
        for (var i = 0; i < dots.length; i++) {
            if (i !== target) {
                dots[i].classList.remove("active");       //增長表示高亮的類名
            } else {
                dots[i].classList.add("active");      //去掉高亮
            }
        }
    }
複製代碼

注意: 因爲點擊底部提示圓點,會使得記號index有跳動,即不是index的變化不在1以內,不只僅是+1-1的變化,這一點須要尤爲注意。自行滾動函數是無限循環執行的,當點擊過底部提示圓點後,記號值倍打亂,因此要將index值返回出去,以便自行滾動函數能夠獲取:

function clickDots(){
        for(var i = 0; i < dots.length; i++){
            (function(n){
                dots[n].onclick = function(){
                    container.style.left = -n*oneImgWidth + "px";
                    index = n;
                    showDots(index);
                }
            })(i);
        }
        return index;       //返回記號值
    }
複製代碼

功能整合

如今終於大功告成了,每一部分的功能都已經實現,如今就只須要將全部的函數整合了,別掉以輕心,整合函數依然是一個不可忽視的步驟,這其中你可能會發現一些問題,這些問題將大大下降用戶的體驗。那麼如今來開始整合函數。

爲了不出現全局變量,咱們將全部的函數和變量所有放進一個函數中,聲明此函數名爲slideShow(),爲實現HTML文檔加載完就開始執行這個函數,須要使用window.onload()方法:

window.onload = slideShow;
複製代碼

咱們在多個函數中都重複獲取了DOM元素,如今咱們把獲取元素放在slideShow()這個函數的首部:

function autoRullImg() {
    var timer = null; 
    var showContainer = document.getElementsByClassName("showContainer")[0]; 
    var container = document.getElementsByClassName("container")[0];  
    var imgNum = container.children.length;  
    var oneImgWidth = container.children[0].offsetWidth;  
    var leftTriangle = document.getElementsByClassName("left-triangle")[0];
    var rightTriangle = document.getElementsByClassName("right-triangle")[0];
    var dots = document.getElementsByClassName("dots")[0].children;     //獲取提示圓點
    var index = 0;
    
    function showDots(target) {
        var dots = document.getElementsByClassName("dots")[0].children;
        for (var i = 0; i < dots.length; i++) {
            if (i !== target) {
                dots[i].classList.remove("active");       //增長表示高亮的類名
            } else {
                dots[i].classList.add("active");      //去掉高亮
            }
        }
    }

    function movement(offset) {
        var oldLeft = parseInt(container.style.left); 
        var newLeft = oldLeft + offset;
        container.style.left = newLeft + "px";
      }

    
    showContainer.onmouseover = function() {
        clearInterval(timer);
    };
    showContainer.onmouseout = function() {
        rullAuto();
    };

    function clickTriangle() {
        leftTriangle.onclick = function() {
            if (parseInt(container.style.left) >= 0) {
                container.style.left = -(imgNum - 1) * oneImgWidth + "px";
            } else {
                movement(oneImgWidth);     
            }
            index = index - 1;
            if( index < 0){ index = imgNum - 1;}
            showDots(index);
        };
        rightTriangle.onclick = function() {
            if (parseInt(container.style.left) <= -(imgNum - 1) * oneImgWidth) {
                container.style.left = "0px";
            } else {
                movement(-oneImgWidth);
            }
            index = index + 1;
            if( index > imgNum - 1){ index = 0;}
            showDots(index);
        };
    }
    clickTriangle() 

    function clickDots(){
        for(var i = 0; i < dots.length; i++){
            (function(n){
                dots[n].onclick = function(){
                    container.style.left = -n*oneImgWidth + "px";
                    index = n;
                    showDots(index);
                }
            })(i);
        }
        return index;
    }
    clickDots()

    function rullAuto() {
        timer =  setInterval(function(){
            movement(-oneImgWidth);
            index = index + 1;
            if( index > imgNum - 1){ index = 0;}
            showDots(index);
            if (parseInt(container.style.left) <= -imgNum * oneImgWidth) {
                container.style.left = "0px";
            }
        }, 2000); 
    }
    rullAuto();
}
window.onload = autoRullImg;
複製代碼

HTML代碼以下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>img scroll</title>
    <link rel="stylesheet" href=".layout.css">
    <script src="./scrollImg.js"></script>
</head>

<body>
    <div class="imgScroll">
        <div class="showContainer">
            <div class="container">
                <img src="https://dimg02.c-ctrip.com/images/100b11000000qezw729A4_R_1600_10000_Mtg_7.jpg" alt="A cat">
                <img src="https://dimg05.c-ctrip.com/images/100u0x000000le38uA71D_R_1600_10000_Mtg_7.jpg" alt="A dog">
                <img src="https://dimg08.c-ctrip.com/images/100811000000qrlfxA3E0_R_1600_10000_Mtg_7.jpg" alt="dandelion">
            </div>
        </div>
        <div class="dots">
            <span class="dot active"></span>
            <span class="dot"></span>
            <span class="dot"></span>
        </div>
        <div class="left-triangle triangle">
            <span>&lt;</span>
        </div>
        <div class="right-triangle triangle">
            <span>&gt;</span>
        </div>
    </div>

</body>
</html>

複製代碼

CSS代碼以下:

.imgScroll {
  width: 800px;
  margin: 0 auto;
  position: relative;
}
.imgScroll .showContainer {
  width: 800px;
  height: 533px;
  margin: 0 auto;
  overflow: hidden;
  position: relative;
}
.imgScroll .showContainer .container {
  width: 9999px;
  position: absolute;
  left: 0px;
}
.imgScroll .showContainer .container img {
  display: block;
  float: left;
  width: 800px;
}
.imgScroll .triangle {
  position: absolute;
  top: 40%;
  cursor: pointer;
  font-size: 40px;
  color: lightgray;
}
.imgScroll .triangle:hover {
  color: gray;
}
.imgScroll .triangle.left-triangle {
  left: -50px;
}
.imgScroll .triangle.right-triangle {
  right: -50px;
}
.imgScroll .dots {
  width: 100%;
  text-align: center;
  height: 50px;
  line-height: 50px;
  cursor: pointer;
}
.imgScroll .dots .dot {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: lightgray;
  margin-right: 10px;
}
.imgScroll .dots .dot.active {
  background-color: gray;
}

複製代碼
相關文章
相關標籤/搜索