무한으로 즐기는 JavaScript 슬라이드 만들기 : 무한루프&자동변환

DANO PARK·2022년 4월 27일
27
post-thumbnail
post-custom-banner

목표 설정

코드 간소화

<script>
  window.onload = function() {
    const kindWrap = document.querySelector('.kind_wrap');
    const slider = kindWrap.querySelector('.slider');
    const slideLis = slider.querySelectorAll('li')
    const moveButton = kindWrap.querySelector('.arrow');

    /* 주요 변수 초기화 */  
    let currentIdx = 0;
    let translate = 0;
    const speedTime = 500;

    /* CSSOM 셋업 */
    const liWidth = slideLis[0].clientWidth;
    const sliderWidth = liWidth * slideLis.length;
    slider.style.width = `${sliderWidth}px` ;

    /* 리스너 설치하기 */
    moveButton.addEventListener('click', moveSlide);

    /* 슬라이드 실행 */
    function move(D) {
      currentIdx += (-1 * D);
      translate += liWidth * D;
      slider.style.transform = `translateX(${translate}px)`;
      slider.style.transition = `all ${speedTime}ms ease`
  }


    /* 버튼 클릭 */
    function moveSlide(event) {
      event.preventDefault();
      if (event.target.className === 'next') {
        if (currentIdx === slideLis.length -1) return;
          move(-1);
      } else {
          if (currentIdx === 0) return;
            move(1);
        }
    }

  }
</script>

이전 글 슬라이드 코드에서 슬라이드를 실행하는 부분을 따로 함수로 빼내어 작성했고, transition 기능을 CSS에서 빼내어 move() 함수에 추가했다.

무한루프 만들기

무한루프를 만들기 위해서는 li 태그의 첫 번째 부분과 마지막 번째 부분을 복사하여 슬라이드 양 끝에 배치해야한다. 그리고 복사된 슬라이드로 넘어왔을때, 애니메이션을 중단하고 그 자리를 복사된 슬라이드와 같은 슬라이드가 자리를 메꾸도록 코드를 작성해야한다.

예를 들어,

A, B, C,

총 3개의 슬라이드가 있다고 가정하자. 무한루프를 만들고 싶다면, 여기서 처음과 끝 슬라이드를 복사하여 가장자리에 배치해야한다.

C, A, B, C, A

위와 같은 구조가 됐을때, 슬라이드 차례가 맨 끝부분인 복사된 A에 도착했다고 가정해보자. 그럴경우 복사된 A는 애니메이션 적용을 받아 자연스럽게 앞의 C에서 이동해왔을 것이다. 여기서 애니메이션 적용을 제거한 뒤 곧바로 두번째 자리에 있는 A로 이동하게 한다면, 우리 눈에는 A가 움직이지 않고 그대로 있는 모습을 연출할 수 있다. 실제로는 복사된 A에서 원래 A로 위치가 변경됐음에도 불구하고 말이다. 아래의 그림을 보면 더 이해하기 쉬울 것이다.

A-B-C-copyA 구조로 슬라이드가 적용되도록 코드를 작성했다고 하자. 위 그림은 해당 구조에서 C에서 copyA로 넘어가는 순간, 애니메이션 적용을 제거하고 A위치로 옮겨가게 코드를 작성해 출력한 모습이다. 여기서 copyA가 A와 콘텐츠가 똑같다면 우리 눈에는 copyA에서 A로 곧바로 넘어갔다는 사실을 인지하지 못하고 무한히 슬라이드가 동작한다고 느낄 것이다.

이제 앞에서 배운 지식을 바탕을 가지고 코드를 작성해보자.

<script>
  window.onload = function() {
    const kindWrap =  document.querySelector('.kind_wrap');
    const slider = kindWrap.querySelector('.slider');
    const slideLis = slider.querySelectorAll('li')
    const moveButton = kindWrap.querySelector('.arrow');

    /* 클론 */
    const clone1 = slideLis[0].cloneNode(true);
    const cloneLast = slideLis[slideLis.length - 1].cloneNode(true);
    slider.insertBefore(cloneLast, slideLis[0]);
    slider.appendChild(clone1);

    /* 주요 변수 초기화 */  
    let currentIdx = 0;
    let translate = 0;
    const speedTime = 500;

    /* CSSOM 셋업 */
    const sliderCloneLis = slider.querySelectorAll('li');
    const liWidth = slideLis[0].clientWidth;
    const sliderWidth = liWidth * sliderCloneLis.length;
    slider.style.width = `${sliderWidth}px`;
    currentIdx = 1;
    translate = -liWidth;
    slider.style.transform = `translateX(${translate}px)`

    /* 리스너 설치하기 */
    moveButton.addEventListener('click', moveSlide);

    /* 슬라이드 실행 */
    function move(D) {
      currentIdx += (-1 * D);
      translate += liWidth * D;
      slider.style.transform = `translateX(${translate}px)`;
      slider.style.transition = `all ${speedTime}ms ease`
    }

    /* 클릭 버튼 */
    function moveSlide(event) {
      event.preventDefault();
      if (event.target.className === 'next') {
        move(-1);
        if (currentIdx === sliderCloneLis.length -1)
          setTimeout(() => {
            slider.style.transition = 'none';
            currentIdx = 1;
            translate = -liWidth;
            slider.style.transform = `translateX(${translate}px)`;
          }, speedTime);
      } else {
          move(1);
          if (currentIdx === 0) {
            setTimeout(() => {
              slider.style.transition = 'none';
              currentIdx = sliderCloneLis.length -2;
              translate = -(liWidth * currentIdx);
              slider.style.transform = `translateX(${translate}px)`;
            }, speedTime);
          }
        }
    }

  }
</script>

위의 코드를 보면, .cloneNode() 기능을 사용해 첫번째와 마지막 li 태그를 복사한 것을 볼 수 있다. 이후 insertBeforeappendChild를 사용해 기존의 li 태그 앞과 뒤에 배치시켰다.

참고로 insertBefore는 배치할 대상과 배치할 위치를 지정해야하나, appendChild는 배치할 대상만 지정해주면 된다.

이렇게 되면 복사된 li 태그 2개가 새로 생겨났지만. 기존 li 태그 값을 받아오는 slideLis 변수는 그것을 반영하지 못한다. 따라서 복사된 태그와 기존의 태그를 묶어 줄 수 있는 slideCloneLis 변수를 새로 만들었다.

그리고 slideLis 변수를 사용한 부분을 전부 slideCloneLis 변수로 바꿔준다.

이후 if문을 이용해 특정 조건(currentIdx가 0 또는 slideCloneLis 배열 길이와 같을 때)일 경우 setTimeout을 사용해 시간차로 애니메이션을 정지하고, 복사된 슬라이드와 같은 슬라이드로 변경해준다. 이렇게 하면 아래와 같은 무한루프 슬라이드를 만들 수 있다.

자동변환 만들기

setTimeout이 한 번 시간을 지연시키는 함수라면 setInterval은 시간 지연을 여러번 반복시킬 수 있다.

자동변환 슬라이드는 setInterval를 활용해서 만들 수 있다.

<script>
  function sliding() {
    move(-1);
    if (currentIdx === sliderCloneLis.length -1)
        setTimeout(() => {
          slider.style.transition = 'none';
          currentIdx = 1;
          translate = -liWidth;
          slider.style.transform = `translateX(${translate}px)`;
        }, speedTime);
  }

  function showSliding() {
    setInterval(sliding, 1500);
  }

  showSliding();
</script>

기존의 'next' 버튼을 담당했던 코드들을 가져와 sliding() 함수에 그대로 입히고, 이것을 그대로 setInterval 함수로 가져다주면 아래와 같은 자동변환 슬라이드를 만들 수 있다.

끝.

profile
단오해서 단호박!
post-custom-banner

4개의 댓글

comment-user-thumbnail
2022년 4월 27일

개구리가 너무 매력있네요. 슬라이드 계속 보게 되요🙄

답글 달기
comment-user-thumbnail
2022년 5월 7일

와..바닐라 멋지게 만드셨어요

1개의 답글
comment-user-thumbnail
2023년 10월 5일

잘 짜신 코드보고 공부할 수 있어서 영광입니다! 감사합니다!

답글 달기