[자바스크립트] 바닐라JS로 캐러셀기능 구현하기
🔗 레퍼런스
✅ 구현 사항
1. 한 눈에 데이터 3개 노출하기 (swiper-slide)
2. 버튼으로 슬라이드 동작 구현하기 (swipe-button-next / swipe-button-prev)
html
<!-- 슬라이드 영역 -->
<div id="main-banner" class="swiper-container banner">
<div class="main01-nav">
<!-- bullet -->
<div class="swiper-pagination swiper-pagination-bullets">
<span class="swiper-pagination-bullet"></span>
<span class="swiper-pagination-bullet"></span>
<span class="swiper-pagination-bullet"></span>
<span class="swiper-pagination-bullet"></span>
<span class="swiper-pagination-bullet"></span>
</div>
<!-- prev, next -->
<button class="swiper-button-next"></button>
<button class="swiper-button-prev"></button>
</div>
<div class="swiper-wrapper-banner">
<div class="swiper-slide">
<div class="slide-txt">
<h1>제목</h1>
<p>아웃라인 텍스트</p>
<p>텍스트</p>
</div>
<img src="assets/main5.png" alt="banner">
</div>
<div class="swiper-slide">
<div class="slide-txt">
<h1>제목</h1>
<p>아웃라인 텍스트</p>
<p>텍스트</p>
</div>
<img src="assets/main1.png" alt="banner">
</div>
<div class="swiper-slide">
<div class="slide-txt">
<h1>제목</h1>
<p>아웃라인 텍스트</p>
<p>텍스트</p>
</div>
<img src="assets/main2.png" alt="banner">
</div>
<div class="swiper-slide">
<div class="slide-txt">
<h1>제목</h1>
<p>아웃라인 텍스트</p>
<p>텍스트</p>
</div>
<img src="assets/main3.png" alt="banner">
</div>
<div class="swiper-slide">
<div class="slide-txt">
<h1>제목</h1>
<p>아웃라인 텍스트</p>
<p>텍스트</p>
</div>
<img src="assets/main4.png" alt="banner">
</div>
<div class="swiper-slide">
<div class="slide-txt">
<h1>제목</h1>
<p>아웃라인 텍스트</p>
<p>텍스트</p>
</div>
<img src="assets/main5.png" alt="banner">
</div>
<div class="swiper-slide">
<div class="slide-txt">
<h1>제목</h1>
<p>아웃라인 텍스트</p>
<p>텍스트</p>
</div>
<img src="assets/main1.png" alt="banner">
</div>
</div>
</div>
아직 슬라이드 안 텍스트들은 데이터 값을 받아온 상태가 아니라서 임의로 만들어놓았다. 캐러셀은 항상 api를 가져와서 사용했었는데 막상 직접 만드려고하니 머리가 띵했다.. 근데 어떻게.. 해야지.. 상단 구조를 잡아놓고 시작
swiper-container / banner : 최상위 클래스로 캐러셀의 전체 구조
swiper-wrapper-banner : 캐러셀 슬라이드의 전체 구조
swiper-slide : 캐러셀 이미지를 설정
swipe-button-prev / swipe-button-next : 캐러셀 사이드 버튼 설정
css
/* 슬라이드 */
.banner {
width: 100%;
overflow: hidden;
margin: 0 auto 80px;
position: relative;
min-width: 1250px;
max-width: 3840px;
min-height: 555px;
}
.banner .main01-nav {
display: table;
position: absolute;
bottom: 5%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
z-index: 2;
}
.banner .main01-nav button[class^="btn"] {
display: table-cell;
width: 34px;
height: 34px;
vertical-align: middle;
}
.banner .swiper-pagination { /* pagination */
display: table-cell;
position: static;
width: auto;
font-size: 0;
vertical-align: middle;
}
.swiper-pagination {
position: absolute;
text-align: center;
transition: opacity 0.3s;
z-index: 10;
}
.banner .swiper-button-prev {
right: 500px;
background-image: url(../assets/prev.png);
}
.banner .swiper-button-next,
.banner .swiper-button-prev {
position: absolute;
top: -300px;
z-index: 20;
background-repeat: no-repeat;
width: 100px;
height: 100px;
background-color: transparent;
background-size: 100px auto;
font-size: 0;
}
.swiper-button-next,
.swiper-button-prev {
position: absolute;
top: 50%;
z-index: 50;
cursor: pointer;
background-position: 50%;
background-repeat: no-repeat;
}
.banner .swiper-button-next {
left: 500px;
background-image: url(../assets/next.png);
}
우선 슬라이드에 pagination 슬라이드 이미지 가운데 배치 시키고 backgroun-image로 이전/다음 버튼을 넣어주었다.
.swiper-slide > img {
width: 100%;
}
.swiper-wrapper-banner {
position: relative;
width: 100%;
height: 100%;
z-index: 1;
display: flex;
box-sizing: content-box;
transition: 0.3s ease-in-out;
}
.banner .swiper-slide {
float: left;
position: relative;
overflow: hidden;
width: 1250px;
padding: 0 10px;
border-radius: 12px;
}
.swiper-slide {
flex-shrink: 0;
width: 100%;
height: 100%;
position: relative;
}
.slide-txt{
position: absolute;
top:75%;
left: 15%;
transform: translate(-15%, -50%);
color:#fff;
font-size: 40px;
line-height: 1.5em;
font-weight: bolder;
}
display : flex 로 슬라이드들을 가로로 정렬해주고 슬라이드의 가로 사이즈는 1250px로 맞춰주었다. 슬라이드마다 텍스트를 띄우기 위해 slide-txt가 들어가 있는 swiper-slide에 position:relative를 주고 왼쪽 아래 배치해 주었다.
Javascript
const sliderContainer = document.querySelector(".swiper-container");
const sliderWrapper = sliderContainer.querySelector(".swiper-wrapper-banner");
const slides = sliderWrapper.querySelectorAll(".swiper-slide");
const prevBtn = sliderContainer.querySelector(".swiper-button-prev");
const nextBtn = sliderContainer.querySelector(".swiper-button-next");
const pagination = sliderContainer.querySelector(".swiper-pagination");
필요한 요소들을 변수에 저장해준다. .swiper-slide는 배열로 가지고 올거기때문에 querySelectorAll 로 가지고 오자!
const slideCount = slides.length;
const size = slides[0].clientWidth;
let currentIndex = 1;
slideCount에는 swiper-slide의 길이를 저장해주고, size는 슬라이드 0번째에 있는 컨텐츠의 크기를 확인할 수 있는 변수이다. slide의 사이즈는 css에서 1250px로 정했으니 size = 1250px이다.
currentIndex는 현재 위치를 저장하기 위한 변수이고 1번째 이미지를 메인으로 하기 위해 초기값을 1로 설정했다.
function updateSliderPosition() {
sliderWrapper.style.transform = `translateX(${-size * currentIndex + 80}px)`;
}
메인 배너가 배치되는 위치를 조정하는 함수로 updateSliderPosition()을 만들고 위치 값으로 슬라이드 사이즈와 현재 위치에 + 80px한 값을 넣어주었다. size = 1250px / currentIndex = 1 / + 80 으로 -1170 값이 나오는데 이 값은 처음 메인에 3개의 배너가 노출이 되어야 하기때문에 사이드에도 배너가 조금 보일 수 있게 밀어준 값이라고 생각하면 된다.
개발자 도구로 보면 이렇게 transform 으로 밀려있는걸 볼 수 있다!
function goToSlide(index) {
sliderWrapper.style.transition = "0.3s ease";
if (index < 0) {
currentIndex = slideCount - 1;
} else if (index >= slideCount) {
currentIndex = 0;
} else {
currentIndex = index;
}
console.log(index);
updateSliderPosition();
}
다음으로 만들어야할 건 슬라이드를 움직이게 해주는 함수! 인덱스가 범위를 벗어나는지 확인하고 어떤 슬라이드가 노출되는지 조정한다. 이미지 슬라이드의 길이(이미지 장수)보다 커지면 안되므로 슬라이드에 포함된 이미지의 개수만큼만 작동할 수 있도록 인덱스가 슬라이드의 길이보다 크면 0으로 만들도록 했다.
//슬라이드 무한루프
sliderWrapper.addEventListener("transitionend", () => {
// currentIndex가 마지막일 인덱스일 경우
if (currentIndex === slides.length - 1) {
//첫번째 인덱스로 돌아감
currentIndex = 1;
sliderWrapper.style.transition = "0s";
sliderWrapper.style.transform = `translateX(${-size * currentIndex + 80}px)`;
}
// 첫번째 인덱스일 경우
if (currentIndex === 0) {
// 마지막 슬라이드 이전 슬라이드
currentIndex = slides.length - 2;
sliderWrapper.style.transition = "0s";
sliderWrapper.style.transform = `translateX(${-size * currentIndex + 80}px)`;
}
});
이제 슬라이드가 무한으로 돌아가는 루프를 만들어보자. 슬라이드의 이미지가 마지막에 도달했을 때 인덱스를 조정하는 무한 루프 함수를 만들었다. transitionend 이벤트를 사용해서 만들어보았다
✏️ transitionend
transitionend 이벤트는 CSS transition 이 완료되면 발생한다.
transition 속성이 제거되거나 display가 none으로 설정된 경우와 같이 완료 전에
transition이 제거된 경우에는 이벤트가 생성되지 않는다.
currentIndex가 마지막일 인덱스일 경우 슬라이드의 길이에서 -1 한 값과 현재 위치가 같다면 처음 인덱스로 돌아가도록한다. 이때 transition을 0s로 해서 아무런 애니메이션 효과가 없도록한다.
첫번째 인덱스일 경우 마지막 슬라이드가 첫번째의 이전 슬라이드가 되도록 슬라이드 길이에서 -2를 해준다.
nextBtn.addEventListener("click", () => goToSlide(currentIndex + 1));
prevBtn.addEventListener("click", () => goToSlide(currentIndex - 1));
마지막으로 다음 버튼과 이전버튼에 이벤트 리스너를 달면 완성이다. 중간에 막히긴 했는데 첫번째 슬라이드 이미지 = 마지막 슬라이드 이미지를 넣고 마지막 슬라이드 이미지 = 첫번째 슬라이드 이미지를 넣으니까 해결 완료.. 마지막에 갔다가 다시 맨 처음으로 후리리릭 가는게 싫어서 참고사이트를 보고 한 꼼수다 후후.. 왜 이렇게 다들 잘할까.. 난..? 내일은 배너에 실제 api 데이터를 가지고 와야하는데 나 잘할 수 있게찌..
🔗 참고사이트