--- 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">&lt;</div> <div id="next-btn" class="next-btn control-btn">&gt;</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` `輪播`