[shopping mall] Carousel (2021 02 26) - adelakim5/fe-w3-shopping GitHub Wiki

구현한 내용

  1. 기본 캐러셀 with pagination
  2. 롱클릭 캐러셀 (2초동안 누르고 있으면 2개씩 넘기는)

기본 캐러셀

일반적으로 자주 쓰이는 캐러셀인데, 하단에 위치한 점들(편하게 페이지네이션이라고 부를 예정)도 같이 동작하도록 구성한다.

(저번엔 못했지만..) 이번엔 이벤트위임을 익혀서 버튼 동작에 적용시켜보았다.

  1. 캐러셀 구성하기

테스트용으로 위와 같이 세팅해주었다.

위 그림은 다음과 같이 배치한 상태이다.

  1. SlideList: 캐러셀로 움직일 SlideContent들을 나열한 전체
  2. SlideBox: 캐러셀에 보여지는 유일한 공간. 나머지는 보이지 않을 예정
  3. SlideContent: 버튼을 누를때마다 나타날 개별 아이템

그러면 로직은 다음과 같이 짤 수 있다.

1.nextButton을 누르면?

  • 다음 SlideContent가 SlideBox에 나타난다 => SlideContent의 width만큼 왼쪽으로 이동한다.
  1. prevButton을 누르면?
  • 이전 SlideContent가 SlideBox에 나타난다 => SlideContent의 width만큼 오른쪽으로 이동한다.

이동은 무엇으로? transform으로 이동시킨다.

애니메이션 효과는? translate으로 준다. 나는 300ms로 주었다.

let currIndex = 0; // 현재 slideBox에 보여지는 아이템의 index
const slideLen = slideList.childElementCount; // slideList의 자식 개수 === slideContent의 개수 
const speed = 300;
const width = 400;

nextButton.addEventListener("click", () => {
  if(currIndex <= slideLen - 1) {
     slideList.style.transition = `${speed}ms`;
     slideList.style.transform = `translateX(-${width * (currIndex + 1)}px)`; // 다음 아이템이 보이도록 slideList를 왼쪽으로 이동시킴
  }
  currIndex++;
})

이렇게 짜면, 다음 버튼을 클릭시 slideList가 왼쪽으로 400px(slideContent의 너비)만큼 이동하게 된다.

But, 맨 마지막으로 이동했을때 문제가 발생한다. 이때, 맨 처음으로 이동하게끔 만들어야 한다.

nextButton.addEventListener("click", () => {
  if(currIndex < slideLen - 1) {
     slideList.style.transition = `${speed}ms`;
     slideList.style.transform = `translateX(-${width * (currIndex + 1)}px)`; // 다음 아이템이 보이도록 slideList를 왼쪽으로 이동시킴
  } else {
     slideList.style.transform = `translateX(0px)`;
     currIndex = -1;
  }
  currIndex++;
})

근데 이렇게 짜도 문제가 생긴다. 바로... 1번으로 홱 돌아가는 문제 ㅜㅜ

nextButton을 클릭하면 홱 돌아가는게 아니라, 마치 맨 마지막 뒤에 맨 첫번째 아이템이 붙어있는 것처럼 자연스럽게 움직이도록 짜기 위해서는 첫번째와 마지막 아이템을 복사해서 붙여넣어주는 작업을 추가해야 한다.

바로 이런 모습처럼!

const firstChild = slideList.firstElementChild;
const lastChild = slideList.lastElementChild;
const clonedFirst = firstChild.cloneNode(true);
const clonedLast = lastChild.cloneNode(true);

slideList.appendChild(clonedFirst); // slidelist의 마지막에 append
slideList.insertBefore(clonedLast, slideList.firstElementChild); // 제일 첫번째에 마지막 슬라이드 삽입

slideList.style.transform = `translateX(-${width}px)`;
/* 
복사본이 앞뒤로 붙었기 때문에 slideList의 첫번째요소는 마지막 아이템이고, 마지막요소는 첫번째 아이템임
slideBox에 첫번째 아이템이 보여지도록 초기화시키기 위해서는 transform으로 슬라이드를 이동시켜주어야 함
*/

복사본을 붙였기 때문에 tranform으로 이동하는 거리도 조정해주어야 한다.

유의할 점

  1. currIndex = 0, 첫번째 아이템의 index를 0으로 정했다면, 첫번째 아이템 전에 붙은 마지막 아이템의 index = -1
  2. 초기 세팅된 슬라이드는 {width}px만큼 왼쪽으로 이동한 상태이므로, left의 초기상태가 0이 아닌 -width

따라서 다음과 같이 작성할 수 있다.

// event delegation

buttons.addEventLister("click", ({target}) => {
  // 개별 버튼들에 이벤트 위임
  if(target.classList.contains("btn_prev")) {
     if(currIndex >= 0) {
        slideList.style.transition = `${speed}ms`;
        slideList.style.transform = `translateX(-${width * currIndex}px)`;
     }
     if(currIndex === 0) { // 첫번째 요소, 즉 마지막 아이템이면
        setTimeout(() => {
          slideList.style.transition = `0ms`;
          slideList.style.transform = `translateX(-${width * slideLen}px)`; // 마지막 위치로 이동 
        }, speed)
        currIndex = slideLen;
     }
     --currIndex
  } 

  if(target.classList.contains("btn_next")) {
     if(currIndex <= slideLen-1) {
        slideList.style.transition = `${speed}ms`;
        slideList.style.transform = `translateX(-${width * (currIndex + 2)}px)`;
     }

     if(currIndex === slideLen-1) { // 마지막요소, 즉 첫번째 아이템이면
        setTimeout(() => {
           slideList.style.transition = `0ms`;
           slideList.style.tranform = `translateX(-${width}px)`; // 초기상태로 돌아감
        }, speed);
        currIndex = -1;
     }
     ++currIndex;
  }
})

이렇게하면, 첫번째 아이템의 복제본이거나 마지막 아이템의 복제본일 때, 0.3초 이후 진짜 첫번째 아이템/마지막 아이템이 있는 위치로 이동한다.

단, 이때 transition을 0ms로 함으로써 애니메이션 효과를 제거하고 한번에 이동시킨다 => 자연스럽게 다음 슬라이드로 넘어가는 것처럼 보여진다.

롱클릭 캐러셀은 다음 페이지에서..