캐러셀 슬라이드

J.yeon·2024년 4월 26일

라이브러리 없이 바닐라 스크립트로 캐러셀 슬라이드 만들기

point1✨ n초마다 자동 슬라이드 이동
point2✨ 버튼으로 앞, 뒤 슬라이드 이동



HTML

(생략)
.
.
<div class="swiper">
  <div class="swiper-wrapper">
    <div class="swiper-slide">
      <div class="img-box">
        <a href=""><img src="img1"></a>
      </div>
    </div>
    <div class="swiper-slide">
      <div class="img-box">
        <a href=""><img src="img2"></a>
      </div>
    </div>
    .
    . (축약)
    .
    <div class="swiper-slide">
      <div class="img-box">
        <a href=""><img src="img6"></a>
      </div>
    </div>
  </div> <!-- //swiper-wrapper -->
  <div class="btn-area">
    <button type="button" class="btn-prev"><span class="blind">이전슬라이드</span></button>
    <button type="button" class="btn-next"><span class="blind">다음슬라이드</span></button>
  </div>
</div> <!-- //swiper -->
.
.



CSS

.swiper{
    position: relative;
    overflow: hidden;
}
.swiper-wrapper{
    display: flex;
}
.swiper-slide{
    min-width: 100%;
    max-height: 700px;
}

.img-box img{
    object-fit: cover;
}

.btn-area{
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 100%;
}
button[class*="btn-"]{
    position: absolute;
    top: 0;
    width: 44px;
    height: 44px;
    box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.2);
    border-radius: 50%;
}
.btn-prev{
    left: 60px;
    background: url(../images/ico_left.svg) no-repeat center #fff;
}
.btn-next{
    right: 60px;
    background: url(../images/ico_right.svg) no-repeat center #fff;
}



JS

우선 변수와 초기 세팅을 먼저 해준다.

/* main visual slide */
const swiperWrap = document.querySelector('.swiper-wrapper');
//첫 번째, 마지막 번째 슬라이드 복제
const firstSlideClone = swiperWrap.firstElementChild.cloneNode(true);
const lastSlideClone = swiperWrap.lastElementChild.cloneNode(true);
swiperWrap.appendChild(firstSlideClone); //끝에 붙이기
swiperWrap.prepend(lastSlideClone); //앞에 붙이기

const swiperSlide = document.querySelectorAll('.swiper-slide'),
        slideWidth = swiperSlide[0].offsetWidth, //슬라이드 하나 너비
		slideLength = swiperSlide.length - 2; //슬라이드 개수 (복제 제외)

const btnPrev = document.querySelector('.btn-area .btn-prev'); //이전 버튼
const btnNext = document.querySelector('.btn-area .btn-next'); //다음 버튼

let current = 1; //현재 페이지
let xVal = -slideWidth; //현재 x값

swiperWrap.style.transform = `translateX(${xVal}px)`;

다음 버튼 click: 마지막 페이지에서 다시 첫 번째 슬라이드로 자연스레 넘어가야하고
이전 버튼 click: 첫 번째 페이지에서 마지막 페이지로 자연스레 넘어가야 한다.


cloneNode() 메소드를 사용하여 swiperWrap 의 첫 번째, 마지막 페이지를 각각 복제해 앞 뒤로 붙여준다.

슬라이드의 너비를 slideWidth 에 담아두고
(만약 슬라이드 너비를 100% 또는 100vw로 지정했다면 담을 필요는 없다, 반응형 시 혹시모를 확장성을 고려하여 js로 담아두고 사용하는 것 뿐...)

현재페이지가 어디인지 확인할 current, swiperWrap이 x축으로 이동할 거리값 xVal를 담아준다.

현재 앞뒤로 복제된 페이지를 추가해뒀기때문에 slide의 개수는 6개가 아닌 총 8개가 되어있는 상태다.

swiperWrap[0] 은 복제한 마지막 페이지의 정보와 같음으로, 첫 페이지의 정보를 보여주려면 첫 시작 상태가 swiperWrap[1] 이 보여져야 한다.

즉, slideWidth 너비만큼 swiperWrap이 이동되어있어야함으로 xVal = -slideWidth; x축 시작값을 슬라이드 너비만큼 넣어준다.



btnNext.addEventListener('click', function(){
  	current++
    xVal -= slideWidth;
    swiperWrap.style.transform = `translateX(${xVal}px)`;
    swiperWrap.style.transition = `all 0.3s`

    if(current == swiperSlide.length - 1 ){ //마지막 인덱스 도달시 (복제된 슬라이드)
        setTimeout(()=>{
            swiperWrap.style.transition = `0s`
            swiperWrap.style.transform = `translateX(${-slideWidth}px)`
            xVal = -slideWidth;
        },300)
        current = 1;
    }
})

다음 버튼을 클릭하면 페이지 수current가 추가(변경)되고,
slideWidth만큼 왼쪽으로 이동되야함으로 xVal 값에 slideWidth 너비를 빼준다.
(왼쪽 - , 오른쪽 +)

부드러운 이동을 위해 transition을 같이 준다.

만약 마지막 인덱스에 도달, 즉 복제된 1번 이미지에 도달하면
빠르게 1번 원본 이미지로 변환되도록 처음 x값인 -slideWidth값 위치로 이동시키고,
xVal = -slideWidth xVal값을 처음 값으로 재할당해준다.

이때 슬라이드가 넘어가자마자 x축 위치를 -slideWidth 로 이동하게되면 뚝 끊기는 어색한 느낌이 생기기때문에 setTimeout 타이머함수를 이용하여 0.3초(transition 속도만큼) 이후에 이동되도록 해준다.



btnPrev.addEventListener('click', function(){
    current--
    xVal += slideWidth;
    swiperWrap.style.transform = `translateX(${xVal}px)`;
    swiperWrap.style.transition = `all 0.3s`

    if(current == 0 ){ //첫 번째 인덱스 도달시 (복제된 슬라이드)
        setTimeout(()=>{
            swiperWrap.style.transition = `0s`
            swiperWrap.style.transform = `translateX(${-slideWidth * slideLength}px)`
            current = slideLength
            xVal = -slideWidth * slideLength;
        }, 300)
        
    }
})

이전 버튼도 다음 버튼과 방식은 똑같다.

다음버튼과는 반대로 페이지 수current가 감소(변경)되고,
slideWidth만큼 오른쪽으로 이동되야함으로 xVal 값에 slideWidth 너비를 더해준다.

만약 첫 번째 인덱스에 도달, 즉 복제된 6번 이미지에 도달하면
빠르게 6번 원본 이미지로 변환되도록 -slideWidth값 * 6 위치로 이동시키고,
current, xVal값을 -slideWidth * 6 로 재할당해준다.
(미리 만들어둔 변수 slideLength = 6)

이제 위 콜백함수들을 사용하기 쉽게 따로 함수로 담아준다👇



// 다음 슬라이드로 넘어가기
let nextSlide = () =>{
    current++
    xVal -= slideWidth;
    swiperWrap.style.transform = `translateX(${xVal}px)`;
    swiperWrap.style.transition = `all 0.3s`

    if(current == swiperSlide.length - 1 ){ //마지막 페이지 도달시
        setTimeout(()=>{
            swiperWrap.style.transition = `0s`
            swiperWrap.style.transform = `translateX(${-slideWidth}px)`
            xVal = -slideWidth;
        },300)
        current = 1;
    }
}

// 이전 슬라이드로 돌아가기
let prevSlide = () =>{
    current--
    xVal += slideWidth;
    swiperWrap.style.transform = `translateX(${xVal}px)`;
    swiperWrap.style.transition = `all 0.3s`

    if(current == 0 ){
        setTimeout(()=>{
            swiperWrap.style.transition = `0s`
            swiperWrap.style.transform = `translateX(${-slideWidth * slideLength}px)`
            current = slideLength
            xVal = -slideWidth * slideLength;
        }, 300)
        
    }
}

setInterval(nextSlide, 5000)
btnNext.addEventListener('click', nextSlide)
btnPrev.addEventListener('click', prevSlide)
/* ---// main visual slide */

5초마다 자동으로 슬라이드가 넘어가게끔 setInterval 타이머함수를 사용하고,
위처럼 변수에 담아둔 함수를 이벤트리스너의 콜백함수로 불러만 오면 슬라이드 완성이다.




✍️평소엔 swiper로만 사용해왔는데 직접 해보니까 간단한 것도 삐걱이게 되는 것 같다.
기능 2개를 따로따로 만들어보다가 합치니까 코드도 달라지게 되고 나름 깔끔한 코드로 만들고자 몇 번 수정했다.

swiper처럼 드래그까지 넣어보고싶었는데 기능 합치려니 계속 오류가 발생하고 해결하지 못했다.
드래그 기능만 넣으면 아주 잘되는데 자동 슬라이드랑 버튼 슬라이드까지 합치려니 어려운 것 같다. 나중에 다시 시도해봐야겠다.

profile
나혼자만 윈도우UP💻

0개의 댓글