이미지 슬라이더 만들기

두 번째 프로젝트로 이미지 슬라이드를 만들어 보자. html과 css를 이용해 이미지 슬라이드를 만들고, next, previous 버튼을 이용해 이미지 이동 버튼을 구현해보자. 그리고 indicator를 개발해서 원하는 index로 이미지가 이동하도록 해보자. 기본 상태가 autoplay 상태로 하고, stop을 할 수 있도록 구현해보자.

autoplay Icon 버튼

npm install --save @fortawesome/fontawesome-free
@import url('~@fortawesome\fontawesome-free\css\all.min.css');
module: {
        rules: [
            {
                test: /\.css$/,
                use: [MiniCssExtractPlugin.loader, "css-loader"]
            },
            {
                test: /\.jpeg$/,
                type: 'asset/inline'
            },
        ]
    },

HTML Architecture


.slider-wrap {
  width: 1000px;
  height: 400px;
  margin: 50px auto;
  position: relative;
  overflow: hidden;
}
.slider-wrap ul.slider {
  width: 100%;
  height: 100%;
  position: absolute;
  left: 0px;
}
.slider-wrap ul.slider li {
  float: left;
  width: 1000px;
  height: 400px;
}
.btn {
  position: absolute;
  width: 50px;
  height: 60px;
  top: 50%;
  margin-top: -25px;
  line-height: 57px;
  text-align: center;
  cursor: pointer;
  background: rgba(0, 0, 0, 0.1);
  z-index: 100;
  user-select: none;
  transition: 0.1s;
}
.next {
  right: -50px;
  border-radius: 7px 0px 0px 7px;
  color: white;
}
.previous {
  left: -50px;
  border-radius: 0px 7px 7px 7px;
  color: white;
}
.slider-wrap:hover .next {
  right: 0px;
}
.slider-wrap:hover .previous {
  left: 0px;
}

next/prev 버튼 구현

index.js

import '../css/style.css';
import '@fortawesome/fontawesome-free/js/all.js';
import { ImageSlider } from './ImageSlider.js';

new ImageSlider();
    assignElement() {
        this.sliderWrapEl = document.getElementById('slider-wrap');
        this.sliderListEl = this.sliderWrapEl.querySelector('#slider');
        this.previousBtnEl = this.sliderWrapEl.querySelector('#previous');
        this.indicatorWrapEl = this.sliderWrapEl.querySelector('#indicator-wrap');
        this.controlWrapEl = this.sliderWrapEl.querySelector('#control-wrap');
    }

    initSliderNumber() {
        this.#sliderNumber = this.sliderListEl.querySelectorAll('li').length;
    }   

    initSliderWidth() {
        this.#sliderWidth = this.sliderWrapEl.clientWidth;
    }
    initSliderWidth() {
        this.#sliderWidth = this.sliderWrapEl.clientWidth;
    }
    addEvent() {
        this.nextBtnEl.addEventListener('click', this.moveToRight.bind(this));
        this.previousBtnEl.addEventListener('click', this.moveToLeft.bind(this));
    }
    moveToRight() {
        this.#currentPosition += 1;
        if (this.#currentPosition === this.#sliderNumber) {
        this.#currentPosition = 0;
        }
        this.sliderListEl.style.left = `-${
        this.#sliderWidth * this.#currentPosition
        }px`;
    }

indicator 구현

<div class="indicator-wrap" id="indicator-wrap">
    <ul>
    </ul>
</div>
createIndicator() {
    const docFragment = document.createDocumentFragment();
    for (let i = 0; i < this.#sliderNumber; i += 1) {
      const li = document.createElement('li');
      li.dataset.index = i;
      docFragment.appendChild(li);
    }
    this.indicatorWrapEl.querySelector('ul').appendChild(docFragment);
}
    setIndicator() {
        // index 활성화 없음
        this.indicatorWrapEl.querySelector('li.active')?.classList.remove('active');
        // index에 따라서 활성화
        this.indicatorWrapEl
        .querySelector(`ul li:nth-child(${this.#currentPosition + 1})`)
        .classList.add('active');
    }

    onClickIndicator(event) {
        const indexPostion = parseInt(event.target.dataset.index, 10);
        if (Number.isInteger(indexPostion)) {
            this.#currentPosition = indexPostion;
            this.sliderListEl.style.left = `-${
            this.#sliderWidth * this.#currentPosition
            }px`;
            this.setIndicator();
        }
    }

autoplay 구현

<div class="control-wrap play" id="control-wrap">
    <i class="fa fa-pause" id="pause" data-status="pause"></i>
    <i class="fa fa-play" id="play" data-status="play"></i>
</div>
.play .fa-play {
  display: none;
}

.play .fa-pause {
  display: block;
}

.pause .fa-play {
  display: block;
}

.pause .fa-pause {
  display: none;
}
this.controlWrapEl = this.sliderWrapEl.querySelector('#control-wrap');
initAutoPlay() {
    this.#intervalId = setInterval(this.moveToRight.bind(this), 3000);
}
    togglePlay(event) {
        console.log(event);
        if (event.target.parentElement.dataset.status === 'play') {
            this.#autoPlay = true;
            this.controlWrapEl.classList.add('play');
            this.controlWrapEl.classList.remove('pause');
            this.initAutoPlay();
        } else if (event.target.parentElement.dataset.status === 'pause') {
            this.#autoPlay = false;
            this.controlWrapEl.classList.add('pause');
            this.controlWrapEl.classList.remove('play');
            clearInterval(this.#intervalId);
        }
    }
moveToRight() {
    this.#currentPosition += 1;
    if (this.#currentPosition === this.#sliderNumber) {
      this.#currentPosition = 0;
    }
    this.sliderListEl.style.left = `-${
      this.#sliderWidth * this.#currentPosition
    }px`;
    if (this.#autoPlay) {
      clearInterval(this.#intervalId);
      this.#intervalId = setInterval(this.moveToRight.bind(this), 3000);
    }
    this.setIndicator();
}

완성본 보러가기

끝!