라이브러리 없이 바닐라 스크립트로 캐러셀 슬라이드 만들기
point1✨ n초마다 자동 슬라이드 이동
point2✨ 버튼으로 앞, 뒤 슬라이드 이동
(생략)
.
.
<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 -->
.
.
.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;
}
우선 변수와 초기 세팅을 먼저 해준다.
/* 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처럼 드래그까지 넣어보고싶었는데 기능 합치려니 계속 오류가 발생하고 해결하지 못했다.
드래그 기능만 넣으면 아주 잘되는데 자동 슬라이드랑 버튼 슬라이드까지 합치려니 어려운 것 같다. 나중에 다시 시도해봐야겠다.