封裝一個原生js的輪播圖。
知識點
- class封裝邏輯。
- 輪播圖窗口滑動。
- 輪播圖無限輪播到下一組或上一組時要瞬間移動回中間組再添加動效回去。
#js
class MkCarousel {
constructor(options) {
//settings參數設定:
//carouselWrapper 外層起點用來綁定dom
//carousel 要設定transform移動的窗口dom
//currentIndex 當前是第幾張
//carouselMoveFlag 可不可以滑動
//time 滑動要花多久時間
//autoPlay 是否自動輪播
this.options = options
//dom
this.carousel = document.querySelector(`${options.carousel}`)
this.carouselWrapper = document.querySelector(`${options.carouselWrapper}`)
this.carouselItems = document.querySelectorAll(`${options.carousel} .carousel-item`)
this.carouselPreBtn = document.querySelector('#carousel-pre')
this.carouselNextBtn = document.querySelector('#carousel-next')
this.timer = null
//event
this.carouselPreBtn.addEventListener('click', () => this.changeCarouselHandler('pre'))
this.carouselPreBtn.addEventListener('mouseenter', () => this.clearTimerHandler())
this.carouselPreBtn.addEventListener('mouseleave', () => this.resetTimerHandler())
this.carouselNextBtn.addEventListener('click', () => this.changeCarouselHandler('next'))
this.carouselNextBtn.addEventListener('mouseenter', () => this.clearTimerHandler())
this.carouselNextBtn.addEventListener('mouseleave', () => this.resetTimerHandler())
this.carousel.addEventListener('transitionend', () => {
this.transitionendHandler()
})
this.carouselItems.forEach(dom => {
dom.addEventListener('mouseenter', () => this.clearTimerHandler())
})
this.carouselItems.forEach(dom => {
dom.addEventListener('mouseleave', () => this.resetTimerHandler())
})
window.addEventListener('resize', this.debounce(this.renderHandler.bind(this)))
}
initHandler() {
this.options.carouselMoveFlag = false
this.options.currentIndex = this.carouselItems.length / 3 + Math.abs(this.options.currentIndex % 3)
this.carouselItems[this.options.currentIndex].classList.add('active')
this.carousel.style.transition = 'none'
this.renderHandler()
setTimeout(() => {
this.options.carouselMoveFlag = true
this.carousel.style.transition = `transform ${this.options.time / 1000}s`
if (this.options.autoPlay) this.resetTimerHandler()
}, this.options.time)
}
//輪播
resetTimerHandler() {
if (this.timer) return
this.timer = setInterval(() => {
this.changeCarouselHandler('next')
}, this.options.time * 2)
}
//停止輪播
clearTimerHandler() {
window.clearInterval(this.timer)
this.timer = null
}
//移動窗口位置
renderHandler() {
//計算當前選到的圖片置中時的位置
let offsetX = -(this.options.currentIndex * this.carouselItems[0].getBoundingClientRect().width - this.carouselWrapper.getBoundingClientRect().width / 2 + this.carouselItems[0].getBoundingClientRect().width / 2)
this.carousel.style.transform = `translateX(${offsetX}px)`
}
//動畫結束後處理主要就是要先消除transition後瞬間切換位置在加回去
transitionendHandler() {
this.carouselItems.forEach(dom => dom.classList.remove('active'))
if (this.options.currentIndex < this.carouselItems.length / 3) {
this.options.currentIndex = (this.carouselItems.length / 3) * 2 - 1
this.carousel.style.transition = 'none'
this.carouselItems[this.options.currentIndex].classList.add('active')
this.renderHandler()
setTimeout(() => {
this.carousel.style.transition = `transform ${this.options.time / 1000}s`
this.options.carouselMoveFlag = true
})
return
}
if (this.options.currentIndex > (this.carouselItems.length / 3) * 2 - 1) {
this.options.currentIndex = this.carouselItems.length / 3
this.carousel.style.transition = 'none'
this.carouselItems[this.options.currentIndex].classList.add('active')
this.renderHandler()
setTimeout(() => {
this.carousel.style.transition = `transform ${this.options.time / 1000}s`
this.options.carouselMoveFlag = true
})
return
}
this.options.carouselMoveFlag = true
this.carouselItems[this.options.currentIndex].classList.add('active')
}
//上一張 下一張
changeCarouselHandler(direction) {
if (!this.options.carouselMoveFlag) return
this.options.carouselMoveFlag = false
if (direction === 'pre') {
this.options.currentIndex -= 1
}
if (direction === 'next') {
this.options.currentIndex += 1
}
this.renderHandler()
}
debounce(func, delay = 250) {
let timer = null;
return (e) => {
clearTimeout(timer);
timer = setTimeout(() => func(e), delay);
};
}
}
#html
<div id="app">
<div class="carousel-wrapper" id="carousel-wrapper">
<ul class="carousel" id="carousel">
<li class="carousel-item" v-for="item of newArr" :key="item">
<div class="carousel-item-content">{{item}}</div>
</li>
</ul>
<button id="carousel-pre">pre</button>
<button id="carousel-next">next</button>
</div>
</div>
#css
* {
box-sizing: border-box;
}
.carousel-wrapper {
position: relative;
width: 700px;
display: inline-block;
overflow: hidden;
}
.carousel {
display: flex;
width: 100%;
flex-wrap: nowrap;
}
.carousel-item {
background-color: red;
min-width: 700px;
padding: 10px;
}
.carousel-item.active {
background-color: blue;
padding: 100px;
}
.carousel-item-content {
background-color: #fff;
}
#vue中使用
const app = {
data() {
return {
options: {
carouselWrapper: '#carousel-wrapper', //最外層dom必須
carousel: '#carousel', //窗戶dom必須
currentIndex: 2, //起始中間要是那一張圖片
carouselMoveFlag: true, //可否移動
time: 2000, //持續時間
autoPlay: true //是否自動輪播
},
arr: [1, 2, 3]
}
},
mounted() {
new MkCarousel(this.options).initHandler()
},
computed: {
//前後要複製一組總共有三組
newArr() {
return [...this.arr, ...this.arr, ...this.arr]
}
}
}
Vue.createApp(app).mount('#app')