js寫一個輪播圖吧!

  • 206
  • 0

封裝一個原生js的輪播圖。

知識點

  1. class封裝邏輯。
  2. 輪播圖窗口滑動。
  3. 輪播圖無限輪播到下一組或上一組時要瞬間移動回中間組再添加動效回去。
#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')