最近看了一組故宮的圖片,覺得很大氣,決定選用故宮的圖片用來展示,如下:
樣式構成與上一節相似,這裡稍做調整,便不再贅述。
輪播圖的動畫效果,就是圖片按照規定的方向,不停滾動以展示每一張圖片,同時還可以通過點選以改變原本的展示順序。實現有一下幾點:
- 不做點選操作,圖片自行滾動(這裡我們規定自右向左滾動);
- 點選左右箭頭,實現圖片翻頁;
- 點選提示圓點,顯示不同圖片;
- 功能整合。
自動迴圈滾動
圖片自行滾動務必就要使用到定時器setInterval
或者setTimeout
,使得移動函式每隔一段時間執行一次,以達到迴圈滾動的動畫效果。
定時器的介紹
-
setInterval()
方法會重複呼叫一個函式或執行一個程式碼段,在每次呼叫之間具有固定的時間延遲,返回一個intervalID
,可以作為引數傳給clearInterval()
來取消該定時器。 -
setTimeout()
方法設定一個定時器,在定時器到期後會執行一個函式或一段程式碼,返回值timeoutID
是一個正整數,表示定時器的編號。這個值可以傳遞給clearTimeout()
來取消該定時器。
setInterval( )
let intervalID = window.setInterval(func, delay);
複製程式碼
intervalID
是此重複操作的唯一辨識符,可以作為引數傳給clearInterval()
。func
是你想要重複呼叫的函式。
setTimeout( )
var timeoutID = scope.setTimeout(function, delay);
複製程式碼
function
是你想要在delay毫秒之後執行的函式。- 延遲的毫秒數 (一秒等於1000毫秒),函式的呼叫會在該延遲之後發生。
例項研究
首先,理解圖片的迴圈滾動,實際上就是多次連續的移動,每次移動固定的距離,若干次的移動,就形成了迴圈效果,那麼此處我們應該首個實現的是移動一次的效果,即移動函式--movefunc
。
我們先回顧一下各個容器之間的層級關係:
<!--最外層的父級容器-->
<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><</span>
</div>
<div class="right-triangle triangle">
<span>></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();
};
}
複製程式碼
這樣作為一個完整的函式就拿過了使用,一定會讓你大失所望,圖片滾著滾著就不見了,container
的style.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><</span>
</div>
<div class="right-triangle triangle">
<span>></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;
}
複製程式碼