---
tags: Example,
disqus: hackmd
---
# JavaScript Slider 輪播圖、燈箱練習
這是第一次用Vanilla JS來寫一個輪播+燈箱。
也是第一次寫可以loop的輪播。
所以有很多地方還不夠好,多包涵。
本次參考的輪播燈箱範例[blueimp Gallery](http://blueimp.github.io/Gallery/)
## 程式碼
### html
```htmlmixed=
<html>
<head>
<title>Lightbox Image Gallery</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="index.css">
</head>
<body>
<div class="gallery-wrap clearfix">
<div class="gallery-box">
<img src="image/small/49924174576_aac29e826c_s.jpg" alt="">
</div>
<div class="gallery-box">
<img src="image/small/49924541758_0898ab1f3b_s.jpg" alt="">
</div>
<div class="gallery-box">
<img src="image/small/49925577287_4b7ec83916_s.jpg" alt="">
</div>
<div class="gallery-box">
<img src="image/small/49926242418_3f21cf8e52_s.jpg" alt="">
</div>
<div class="gallery-box">
<img src="image/small/49928768098_2bef6a3084_s.jpg" alt="">
</div>
<div class="gallery-box">
<img src="image/small/49929403292_389f0233f7_s.jpg" alt="">
</div>
<div class="gallery-box">
<img src="image/small/49929612551_b6dc1392fa_s.jpg" alt="">
</div>
<div class="gallery-box">
<img src="image/small/49929715321_a38d609252_s.jpg" alt="">
</div>
<div class="gallery-box">
<img src="image/small/49929759196_0243bd30b2_s.jpg" alt="">
</div>
<div class="gallery-box">
<img src="image/small/49929923203_aed632ac6c_s.jpg" alt="">
</div>
<div class="gallery-box">
<img src="image/small/49930650242_73129d5e44_s.jpg" alt="">
</div>
<div class="gallery-box">
<img src="image/small/49930937571_62c0709276_s.jpg" alt="">
</div>
<div class="gallery-box">
<img src="image/small/49930991576_a05a942aa0_s.jpg" alt="">
</div>
</div>
<div class="lightbox-wrap">
<div id="close-btn" class="close-btn">X</div>
<div id="prev-btn" class="prev-btn control-btn"><</div>
<div id="next-btn" class="next-btn control-btn">></div>
<div class="photo-frame clearfix">
<div class="photo-box">
<img draggable="false" class="photo-box-img" src="image/49924174576_aac29e826c_b.jpg" alt="">
</div>
<div class="photo-box">
<img draggable="false" class="photo-box-img" src="image/49924541758_0898ab1f3b_b.jpg" alt="">
</div>
<div class="photo-box">
<img draggable="false" class="photo-box-img" src="image/49925577287_4b7ec83916_b.jpg" alt="">
</div>
<div class="photo-box">
<img draggable="false" class="photo-box-img" src="image/49926242418_3f21cf8e52_b.jpg" alt="">
</div>
<div class="photo-box">
<img draggable="false" class="photo-box-img" src="image/49928768098_2bef6a3084_b.jpg" alt="">
</div>
<div class="photo-box">
<img draggable="false" class="photo-box-img" src="image/49929403292_389f0233f7_b.jpg" alt="">
</div>
<div class="photo-box">
<img draggable="false" class="photo-box-img" src="image/49929612551_b6dc1392fa_b.jpg" alt="">
</div>
<div class="photo-box">
<img draggable="false" class="photo-box-img" src="image/49929715321_a38d609252_b.jpg" alt="">
</div>
<div class="photo-box">
<img draggable="false" class="photo-box-img" src="image/49929759196_0243bd30b2_b.jpg" alt="">
</div>
<div class="photo-box">
<img draggable="false" class="photo-box-img" src="image/49929923203_aed632ac6c_b.jpg" alt="">
</div>
<div class="photo-box">
<img draggable="false" class="photo-box-img" src="image/49930650242_73129d5e44_b.jpg" alt="">
</div>
<div class="photo-box">
<img draggable="false" class="photo-box-img" src="image/49930937571_62c0709276_b.jpg" alt="">
</div>
<div class="photo-box">
<img draggable="false" class="photo-box-img" src="image/49930991576_a05a942aa0_b.jpg" alt="">
</div>
</div>
<div class="lightbox-img-wall clearfix">
<div class="dot-box">
<img src="image/small/49924174576_aac29e826c_s.jpg" alt="">
</div>
<div class="dot-box">
<img src="image/small/49924541758_0898ab1f3b_s.jpg" alt="">
</div>
<div class="dot-box">
<img src="image/small/49925577287_4b7ec83916_s.jpg" alt="">
</div>
<div class="dot-box">
<img src="image/small/49926242418_3f21cf8e52_s.jpg" alt="">
</div>
<div class="dot-box">
<img src="image/small/49928768098_2bef6a3084_s.jpg" alt="">
</div>
<div class="dot-box">
<img src="image/small/49929403292_389f0233f7_s.jpg" alt="">
</div>
<div class="dot-box">
<img src="image/small/49929612551_b6dc1392fa_s.jpg" alt="">
</div>
<div class="dot-box">
<img src="image/small/49929715321_a38d609252_s.jpg" alt="">
</div>
<div class="dot-box">
<img src="image/small/49929759196_0243bd30b2_s.jpg" alt="">
</div>
<div class="dot-box">
<img src="image/small/49929923203_aed632ac6c_s.jpg" alt="">
</div>
<div class="dot-box">
<img src="image/small/49930650242_73129d5e44_s.jpg" alt="">
</div>
<div class="dot-box">
<img src="image/small/49930937571_62c0709276_s.jpg" alt="">
</div>
<div class="dot-box">
<img src="image/small/49930991576_a05a942aa0_s.jpg" alt="">
</div>
</div>
</div>
</body>
<script src="index.js"></script>
</html>
```
### CSS
```css=
body {
margin: 0;
background: #212121;
}
.clearfix:after {
content: "";
display: table;
clear: both;
}
/* 外部圖片牆 */
.gallery-wrap {
margin: 0 auto;
width: 975px;
}
.gallery-box {
float: left;
width: 75px;
height: 75px;
cursor: pointer;
}
.gallery-box img {
max-width: 100%;
}
/* 燈箱 */
.lightbox-wrap {
position: fixed;
left: 0;
top: 0;
background-color: #000;
width: 100%;
height: 100%;
z-index: -1;
opacity: 0;
}
.lightbox-wrap.open {
animation: openLightBox 1s ease normal both;
}
.lightbox-wrap.close {
animation: closeLightBox 1s ease normal both;
}
@keyframes openLightBox {
from {
z-index: 0;
opacity: 0;
}
to {
z-index: 0;
opacity: 1;
}
}
@keyframes closeLightBox {
from {
z-index: 0;
opacity: 1;
}
to {
z-index: -1;
opacity: 0;
}
}
.control-btn {
position: absolute;
top: 50%;
border-radius: 50%;
border: solid 2px #CCC;
width: 40px;
height: 40px;
cursor: pointer;
color: #CCC;
font-size: 20px;
text-align: center;
line-height: 40px;
z-index: 1;
transform: translate(0, -50%);
}
.control-btn:hover {
border-color: #FFF;
color: #FFF;
}
.prev-btn {
left: 15px;
}
.next-btn {
right: 15px;
}
.close-btn {
position: fixed;
right: 20px;
top: 20px;
cursor: pointer;
color: #CCC;
font-size: 20px;
z-index: 1;
}
.close-btn:hover {
color: #FFF;
}
.lightbox-img-wall {
position: absolute;
left: 0;
right: 0;
bottom: 0;
margin: 0 auto;
max-width: 1000px;
text-align: center;
}
.lightbox-wrap .dot-box {
position: relative;
border-radius: 50%;
width: 20px;
height: 20px;
cursor: pointer;
overflow: hidden;
display: inline-block;
}
.lightbox-wrap .dot-box.current:after,
.lightbox-wrap .dot-box:hover:after {
position: absolute;
left: 0;
top: 0;
border: solid 2px #FFF;
border-radius: 50%;
box-sizing: border-box;
width: 100%;
height: 100%;
content: '';
display: block;
}
.lightbox-wrap .dot-box img {
max-width: 100%;
}
.photo-frame {
height: 100%;
}
.photo-box {
position: relative;
top: 0;
left: 0;
float: left;
text-align: center;
white-space: nowrap;
}
.photo-box .photo-box-img {
max-width: 100%;
max-heiht: 100%;
vertical-align: middle;
}
.photo-box:before {
width: 0;
height: 100%;
content: '';
display: inline-block;
vertical-align: middle;
}
.photo-box:after {
width: 0;
height: 100%;
content: '';
display: inline-block;
vertical-align: middle;
}
```
### JS
```javascript=
function bindGalleryIndex() {
var galleryBox = document.getElementsByClassName('gallery-box');
for (let index = 0; index < galleryBox.length; index++) {
const element = galleryBox[index];
element.setAttribute('data-index', index);
handleGalleryClick(element);
}
}
function bindDotIndex() {
var dotBox = document.getElementsByClassName('dot-box');
for (let index = 0; index < dotBox.length; index++) {
const element = dotBox[index];
element.setAttribute('data-index', index);
handleDotClick(element);
}
}
function bindPhotoIndex() {
var photoBox = document.getElementsByClassName('photo-box');
var photoFrame = document.getElementsByClassName('photo-frame')[0];
/* 計算photoFrame總長 */
photoFrame.style.width = photoBox.length * windowInnerWidth;
for (let index = 0; index < photoBox.length; index++) {
const element = photoBox[index];
element.setAttribute('data-index', index);
element.style.left = -(index * windowInnerWidth);
element.style.width = windowInnerWidth;
/* 滑鼠判斷事件 */
handleMouseMove(photoBox[index]);
}
}
function handleGalleryClick(galleryPhoto) {
/* 點擊的時候取目標的data-index */
galleryPhoto.addEventListener('click', () => {
const galleryPhotoIndex = parseInt(galleryPhoto.getAttribute('data-index'), 10);
const lightBox = document.getElementsByClassName('lightbox-wrap')[0];
/* 更新globalIndex的index */
globalIndex = galleryPhotoIndex;
countTranslate(globalIndex);
dotStyle(globalIndex);
/* 開啟燈箱 */
lightBox.classList.remove('close');
lightBox.classList.add('open');
});
}
function handleDotClick(dot) {
/* 點擊的時候取目標的data-index */
dot.addEventListener('click', () => {
switchPhotoEnd = false;
const dotIndex = parseInt(dot.getAttribute('data-index'), 10);
/* 更新前的globalIndex,也就是dot被點擊前的globalIndex */
beforeUpdateIndex = globalIndex;
/* 更新globalIndex,也就是dot被點擊後的globalIndex */
globalIndex = dotIndex;
countTranslate(globalIndex, beforeUpdateIndex, true);
dotStyle(globalIndex);
});
}
function bindCloseBtn() {
/* 燈箱內的關閉按鈕 */
const closeBtn = document.getElementById('close-btn');
closeBtn.addEventListener('click', () => {
closeEvent()
});
}
function closeEvent() {
/* 關閉燈箱 */
const lightBox = document.getElementsByClassName('lightbox-wrap')[0];
lightBox.classList.remove('open');
lightBox.classList.add('close');
};
function handleMouseMove(element) {
let click = false;
let move = false;
let xPosition = 0;
let yPosition = 0;
let closeStatus = false; /* 關閉燈箱狀態 */
let horizontalScroll = false; /* 是否為左右滑 */
let goNext = null; /* 下一張或上一張狀態 */
element.addEventListener('mousedown', (e) => {
click = true;
var e = e || window.event;
xPosition = e.pageX;
yPosition = e.pageY;
}, false);
element.addEventListener('mousemove', function(e){
/* 判斷滑鼠為左鍵按下狀態,且切換動畫狀態已結束(true),滿足兩條件才執行拖拉判斷 */
if (click && switchPhotoEnd) {
move = true;
var photoBox = document.getElementsByClassName('photo-box');
var photoBoxLength = photoBox.length;
var e = e || window.event;
if (e.pageX > xPosition || e.pageX < xPosition) {
/* 代表左右滑動觸發 */
var distance = e.pageX - xPosition;
horizontalScroll = true;
photoBox[globalIndex].style.transform = 'translate('+ distance +'px, 0px)';
photoBox[globalIndex].style.transition = transformSec;
if (e.pageX > xPosition) {
/* 往右(前一張),且判斷是否為第一張 */
goNext = false;
if (globalIndex === 0) {
photoBox[photoBoxLength - 1].style.transform = 'translate('+ (distance - windowInnerWidth) +'px, 0px)';
photoBox[photoBoxLength - 1].style.transition = transformSec;
} else {
photoBox[globalIndex - 1].style.transform = 'translate('+ (distance - windowInnerWidth) +'px, 0px)';
photoBox[globalIndex - 1].style.transition = transformSec;
}
} else {
/* 往左(下一張),且判斷是否為最後一張 */
goNext = true;
if (globalIndex === photoBoxLength - 1) {
photoBox[0].style.transform = 'translate('+ (distance + windowInnerWidth) +'px, 0px)';
photoBox[0].style.transition = transformSec;
} else {
photoBox[globalIndex + 1].style.transform = 'translate('+ (distance + windowInnerWidth) +'px, 0px)';
photoBox[globalIndex + 1].style.transition = transformSec;
}
}
} else if ((e.pageY > yPosition) && !horizontalScroll) {
/* 代表下滑動觸發 */
element.style.transform = 'translate(0px, '+ (e.pageY - yPosition) +'px)';
closeStatus = true;
}
}
}, false);
element.addEventListener('mouseup', (e) => {
click = false;
if (move !== true) {
if (e.eventPhase === 2 && switchPhotoEnd) {
closeEvent();
}
} else {
move = false;
switchPhotoEnd = false;
var photoBox = document.getElementsByClassName('photo-box');
var photoBoxLength = photoBox.length;
/* 左右滑的時候會給一個horizontalScroll的狀態 */
if (horizontalScroll) {
horizontalScroll = false;
/* 不是去下一張就是回上一張 */
if (goNext) {
if (globalIndex === photoBoxLength - 1) {
photoBox[globalIndex].style.transform = 'translate(-'+ windowInnerWidth +'px, 0)';
photoBox[0].style.transform = 'translate(0, 0)';
photoBox[0].style.transition = transformSec;
globalIndex = 0;
photoBox[0].addEventListener('transitionend', () => {
countTranslate(globalIndex);
});
} else {
photoBox[globalIndex].style.transform = 'translate(-'+ windowInnerWidth +'px, 0)';
photoBox[globalIndex + 1].style.transform = 'translate(0, 0)';
photoBox[globalIndex + 1].style.transition = transformSec;
globalIndex = globalIndex + 1;
}
} else {
if (globalIndex === 0) {
photoBox[globalIndex].style.transform = 'translate('+ windowInnerWidth +'px, 0)';
photoBox[photoBoxLength - 1].style.transform = 'translate(0, 0)';
photoBox[photoBoxLength - 1].style.transition = transformSec;
globalIndex = photoBoxLength - 1;
photoBox[globalIndex].addEventListener('transitionend', () => {
countTranslate(globalIndex);
});
} else {
photoBox[globalIndex].style.transform = 'translate('+ windowInnerWidth +'px, 0)';
photoBox[globalIndex - 1].style.transform = 'translate(0, 0)';
photoBox[globalIndex - 1].style.transition = transformSec;
globalIndex = globalIndex - 1;
}
}
dotStyle(globalIndex);
photoBox[globalIndex].style.transition = transformSec;
photoBox[globalIndex].addEventListener('transitionend', () => switchPhotoEnd = true, false);
}
/* 在確認為上下滑且非左右滑的時候,mouseup關閉燈箱 */
if (closeStatus && !horizontalScroll) {
closeStatus = false;
closeEvent();
}
}
}, false);
}
function controlBtn() {
/* 左右按鈕點擊切換圖片 */
var prevBtn = document.getElementById('prev-btn');
var nextBtn = document.getElementById('next-btn');
var photoBox = document.getElementsByClassName('photo-box');
var photoBoxLength = photoBox.length;
nextBtn.addEventListener('click', () => {
switchPhotoEnd = false;
if (globalIndex === photoBoxLength - 1) {
photoBox[globalIndex].style.transform = 'translate(-'+ windowInnerWidth +'px, 0)';
photoBox[globalIndex].style.transition = transformSec;
photoBox[0].style.transform = 'translate(0, 0)';
photoBox[0].style.transition = transformSec;
globalIndex = 0;
photoBox[0].addEventListener('transitionend', () => {
countTranslate(globalIndex);
});
} else {
photoBox[globalIndex].style.transform = 'translate(-'+ windowInnerWidth +'px, 0)';
photoBox[globalIndex].style.transition = transformSec;
photoBox[globalIndex + 1].style.transform = 'translate(0, 0)';
photoBox[globalIndex + 1].style.transition = transformSec;
globalIndex = globalIndex + 1;
}
dotStyle(globalIndex);
photoBox[globalIndex].addEventListener('transitionend', () => switchPhotoEnd = true, false);
});
prevBtn.addEventListener('click', () => {
switchPhotoEnd = false;
if (globalIndex === 0) {
photoBox[photoBoxLength - 1].style.transform = 'translate(0, 0)';
photoBox[photoBoxLength - 1].style.transition = transformSec;
photoBox[0].style.transform = 'translate('+ windowInnerWidth +'px, 0)';
photoBox[0].style.transition = transformSec;
globalIndex = photoBoxLength - 1;
photoBox[0].addEventListener('transitionend', () => {
countTranslate(globalIndex);
});
} else {
photoBox[globalIndex].style.transform = 'translate('+ windowInnerWidth +'px, 0)';
photoBox[globalIndex].style.transition = transformSec;
photoBox[globalIndex - 1].style.transform = 'translate(0, 0)';
photoBox[globalIndex - 1].style.transition = transformSec;
globalIndex = globalIndex - 1;
}
dotStyle(globalIndex)
photoBox[globalIndex].addEventListener('transitionend', () => switchPhotoEnd = true, false);
});
}
function countTranslate(targetIndex, beforeIndex, transition) {
/* 計算每一個photo-box的位置 */
var photoBox = document.getElementsByClassName('photo-box');
var photoFrame = document.getElementsByClassName('photo-frame')[0];
photoFrame.style.width = photoBox.length * windowInnerWidth;
for (let index = 0; index < photoBox.length; index++) {
const element = photoBox[index];
const elementIndex = parseInt(element.getAttribute('data-index'), 10);
element.style.left = -(index * windowInnerWidth);
element.style.width = windowInnerWidth;
if (targetIndex > elementIndex) {
element.style.transform = 'translate(-'+ windowInnerWidth +'px, 0)';
} else if (targetIndex === elementIndex) {
element.style.transform = 'translate(0, 0)';
} else {
element.style.transform = 'translate('+ windowInnerWidth +'px, 0)';
}
element.style.transition = 'transform 0s';
}
if (beforeIndex) {
photoBox[beforeIndex].style.transition = transformSec;
photoBox[targetIndex].style.transition = transformSec;
}
photoBox[targetIndex].addEventListener('transitionend', () => switchPhotoEnd = true, false);
}
function dotStyle(targetIndex) {
var dotBox = document.getElementsByClassName('dot-box');
for (let index = 0; index < dotBox.length; index++) {
dotBox[index].classList.remove('current');
}
dotBox[targetIndex].classList.add('current');
}
window.onload = (function() {
transformSec = 'transform .5s';
windowInnerWidth = window.innerWidth;
switchPhotoEnd = true;
globalIndex = 0;
bindGalleryIndex();
bindDotIndex();
bindPhotoIndex();
bindCloseBtn();
controlBtn();
window.addEventListener('resize', () => {
windowInnerWidth = window.innerWidth;
countTranslate(globalIndex);
});
})();
```
---
###### tags: `Example` `slider` `輪播`