완성 화면
코드 보기 / CSS
.slider__wrap {
width: 100%;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.slider__img { /*이미지가 나타나는(보이는) 영역*/
position: relative;
width: 800px;
height: 450px;
overflow: hidden;
}
.slider__inner { /*이미지가 슬라이드 되는(움직이는) 영역*/
display: flex;
flex-wrap: wrap;
width: 4800px;
height: 450px;
}
.slider {
position: relative;
width: 800px;
height: 450px;
}
.slider__btn a{
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 60px;
height: 60px;
/* background-color: #fff; */
}
.slider__btn a.previous {
left: 0;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='icon icon-tabler icon-tabler-chevrons-left' width='60' height='60' viewBox='0 0 24 24' stroke-width='2' stroke='%23ffffff' fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath stroke='none' d='M0 0h24v24H0z' fill='none'/%3E%3Cpolyline points='11 7 6 12 11 17' /%3E%3Cpolyline points='17 7 12 12 17 17' /%3E%3C/svg%3E");
text-indent: -99999px;
}
.slider__btn a.next {
right: 0;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='icon icon-tabler icon-tabler-chevrons-right' width='60' height='60' viewBox='0 0 24 24' stroke-width='2' stroke='%23ffffff' fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath stroke='none' d='M0 0h24v24H0z' fill='none'/%3E%3Cpolyline points='7 7 12 12 7 17' /%3E%3Cpolyline points='13 7 18 12 13 17' /%3E%3C/svg%3E");
text-indent: -99999px;
}
.slider__btn a.previous:hover {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='icon icon-tabler icon-tabler-chevrons-left' width='60' height='60' viewBox='0 0 24 24' stroke-width='2' stroke='%23000000' fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath stroke='none' d='M0 0h24v24H0z' fill='none'/%3E%3Cpolyline points='11 7 6 12 11 17' /%3E%3Cpolyline points='17 7 12 12 17 17' /%3E%3C/svg%3E");
}
.slider__btn a.next:hover {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='icon icon-tabler icon-tabler-chevrons-right' width='60' height='60' viewBox='0 0 24 24' stroke-width='2' stroke='%23000000' fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath stroke='none' d='M0 0h24v24H0z' fill='none'/%3E%3Cpolyline points='7 7 12 12 7 17' /%3E%3Cpolyline points='13 7 18 12 13 17' /%3E%3C/svg%3E");
}
.slider__dot {
position: absolute;
left: 50%;
bottom: 20px;
transform: translateX(-50%);
}
.slider__dot .dot {
width: 20px;
height: 20px;
background-color: #fff;
display: inline-block;
border-radius: 50%;
text-indent: -9999px;
transition: all 0.5s;
margin: 3px;
}
.slider__dot .dot.active{
background-color: rgba(0, 0, 0, 1.5);
}
코드 보기 / HTML
<div class="slider__wrap">
<div class="slider__img">
<div class="slider__inner">
<div class="slider s01"><img src="./img/sliderEffect02-min.jpg" alt="이미지1"></div>
<div class="slider s02"><img src="./img/sliderEffect04-min.jpg" alt="이미지2"></div>
<div class="slider s03"><img src="./img/sliderEffect06-min.jpg" alt="이미지3"></div>
<div class="slider s04"><img src="./img/sliderEffect08-min.jpg" alt="이미지4"></div>
<div class="slider s05"><img src="./img/sliderEffect07-min.jpg" alt="이미지5"></div>
</div>
</div>
<div class="slider__btn">
<a href="#" class="previous">previous (이전)</a>
<a href="#" class="next">next (다음)</a>
</div>
<div class="slider__dot">
<!-- <a href="#" class="dot active">1</a>
<a href="#" class="dot">2</a>
<a href="#" class="dot">3</a>
<a href="#" class="dot">4</a>
<a href="#" class="dot">5</a> -->
</div>
</div>
코드 보기 / JAVASCRIPT
const sliderWrap = document.querySelector(".slider__wrap");
const sliderImg = sliderWrap.querySelector(".slider__img");
const sliderInner = sliderWrap.querySelector(".slider__inner");
const slider = sliderWrap.querySelectorAll(".slider");
const sliderDot = sliderWrap.querySelector(".slider__dot");
const sliderBtn = sliderWrap.querySelectorAll(".slider__btn a");
let currentIndex = 0; //현재 보이는 (가장 위에 있는) 이미지
let sliderInterval = 3000; //다음 이미지로 넘어가는 간격의 시간
let sliderCount = slider.length; //전체 이미지의 총 수
let sliderWidth = sliderWrap.querySelector(".slider__img").offsetWidth; //이미지 width 값
let dotIndex = "";
function init() {
//슬라이드 될 이미지만큼 도트 메뉴 생성
slider.forEach(()=>dotIndex += "<a href='#' class='dot'></a>");
sliderDot.innerHTML = dotIndex;
//도트 메뉴 활성화 (첫 번째)
sliderDot.firstChild.classList.add("active");
}
init();
//슬라이드 이미지의 위치 변경
function moveSlider(num){
sliderInner.style.transition = "all 400ms";
sliderInner.style.transform = `translateX(-${sliderWidth * num}px)`;
currentIndex = num;
//버튼 활성화
let DotActive = document.querySelectorAll(".slider__dot .dot");
DotActive.forEach((active)=>active.classList.remove("active"));
DotActive[num].classList.add("active");
}
// 도트 메뉴 클릭 시 이동
sliderDot.querySelectorAll(".dot").forEach((dot, index) => {
dot.addEventListener("click", () => {
moveSlider(index);
});
});
//슬라이드 버튼 클릭 시
sliderBtn.forEach((btn, index)=>{
btn.addEventListener("click",()=>{
let previousIndex = (currentIndex + (sliderCount - 1)) % sliderCount;
let nextIndex = (currentIndex + 1) % sliderCount;
if(btn.classList.contains("previous")){
moveSlider(previousIndex);
} else {
moveSlider(nextIndex);
}
});
});
sliderWrap : 슬라이드 이미지를 감싸는 부모 요소
sliderImg : 슬라이드 이미지들을 감싸고 있는 요소
sliderInner : 슬라이드 이미지들을 실제로 이동시키는 요소
slider : 각 슬라이드 이미지를 나타내는 요소들의 배열
sliderDot : 슬라이드 도트 메뉴를 감싸는 요소
sliderBtn : 슬라이드 이전/다음 버튼을 나타내는 요소들의 배열
currentIndex : 현재 보이는 (가장 위에 있는) 이미지의 인덱스
sliderInterval : 다음 이미지로 넘어가는 간격의 시간 (밀리초 단위)
sliderCount : 전체 이미지의 총 개수
sliderWidth : 이미지의 너비 값
dotIndex : 도트 메뉴를 생성하기 위해 사용되는 문자열 변수
init() 함수 : 각 슬라이드 이미지 수만큼 도트 메뉴를 생성하고, 첫 번째 도트 메뉴를 활성화하는 역할을 한다.
moveSlider() 함수 : 슬라이드 이미지의 위치를 변경하고, 현재 보이는 이미지의 인덱스를 업데이트한다.
이 때, 도트 메뉴에서도 현재 보이는 이미지의 인덱스에 해당하는 도트 메뉴를 활성화합니다.
sliderDot.querySelectorAll(".dot").forEach : sliderDot 요소 내부의 .dot 요소들에 대해 forEach()를 사용하여 클릭 이벤트를 등록한다. 도트 메뉴를 클릭하면 해당 인덱스에 해당하는 슬라이드 이미지를 보여준다.
sliderBtn.forEach((btn, index) : sliderBtn 요소들에 대해서도 forEach()를 사용하여 클릭 이벤트를 등록한다.
이전/다음 버튼을 클릭하면 이전/다음 슬라이드 이미지를 보여준다.
previousIndex와 nextIndex는 이전/다음 슬라이드 이미지의 인덱스를 계산하는데 사용된다.
이 때, % 연산자를 사용하여 인덱스의 범위가 슬라이드 이미지 수 내에 유지되도록 한다.
init()
slider.forEach(()=>dotIndex += "<a href='#' class='dot'></a>");
: slider 변수에 할당된 이미지들의 개수만큼 dotIndex 변수에 문자열을 추가한다.
이 문자열은 각 이미지에 해당하는 도트 메뉴를 생성하는 HTML 코드가 된다.
sliderDot.innerHTML = dotIndex;
: sliderDot 변수에 할당된 도트 메뉴를 가지고 있는 HTML 요소에 dotIndex 문자열을 할당하여 도트 메뉴를 생성한다.
sliderDot.firstChild.classList.add("active");
: 첫 번째 도트 메뉴를 활성화합니다.
이를 위해 sliderDot 요소의 첫 번째 자식 요소에 active 클래스를 추가합니다.
moveSlider
sliderInner.style.transition = "all 400ms";
: 슬라이드 이미지가 이동할 때 애니메이션 효과를 추가한다.
all은 모든 속성에 대해 애니메이션을 적용하며, 400ms는 애니메이션 시간을 0.4초로 지정한다.
sliderInner.style.transform = translateX(-${sliderWidth * num}px);
: 슬라이드 이미지를 이동시킨다.
translateX는 요소를 수평 방향으로 이동시키는 CSS 함수이며, - ${sliderWidth * num}px는 이동 거리를 나타낸다. sliderWidth는 슬라이드 이미지의 가로 크기를 나타내며, num은 이동할 이미지의 인덱스이다.
currentIndex = num;
: 현재 보여지는 이미지의 인덱스를 num으로 업데이트 한다.
let DotActive = document.querySelectorAll(".slider__dot .dot");
: 도트 메뉴를 나타내는 HTML 요소를 선택한다.
DotActive.forEach((active)=>active.classList.remove("active"));
:도트 메뉴의 활성화 클래스 active를 모두 제거한다.
DotActive[num].classList.add("active");
: num에 해당하는 이미지에 해당하는 도트 메뉴에 active 클래스를 추가하여 해당 도트 메뉴를 활성화한다.
sliderDot.querySelectorAll(".dot").forEach((dot, index)
sliderDot.querySelectorAll(".dot")
: 도트 메뉴 요소를 모두 선택한다.
forEach((dot, index) => {...})
: 선택된 도트 메뉴 요소마다 콜백 함수를 실행한다.
이때, dot은 각 도트 메뉴 요소를 나타내며, index는 해당 요소의 인덱스를 나타낸다.
dot.addEventListener("click", () => {...})
: 각 도트 메뉴 요소에 클릭 이벤트 핸들러를 등록한다.
moveSlider(index)
: 클릭한 도트 메뉴에 해당하는 이미지를 화면에 보여주도록 moveSlider() 함수를 호출한다.
index는 클릭한 도트 메뉴 요소의 인덱스이며, 이를 moveSlider() 함수의 인자로 전달하여 해당 이미지를 화면에 보여주게 된다.
sliderBtn.forEach((btn, index)
: 이전/다음 버튼을 클릭할 때 실행되는 이벤트 핸들러를 등록한다.
sliderBtn.forEach((btn, index) => {...})
: 이전/다음 버튼 요소를 모두 선택한다.
btn.addEventListener("click", () => {...})
: 각 버튼 요소에 클릭 이벤트 핸들러를 등록한다.
let previousIndex = (currentIndex + (sliderCount - 1)) % sliderCount;
: 이전 버튼을 클릭했을 때 보여줄 이미지의 인덱스를 계산한다.
currentIndex는 현재 화면에 보이는 이미지의 인덱스이며, (sliderCount - 1)은 이미지 배열의 마지막 인덱스를 의미한다.
% sliderCount는 계산 결과가 이미지 배열의 범위를 벗어나지 않도록 인덱스를 조정한다.
let nextIndex = (currentIndex + 1) % sliderCount;
: 다음 버튼을 클릭했을 때 보여줄 이미지의 인덱스를 계산한다.
currentIndex는 현재 화면에 보이는 이미지의 인덱스이며, + 1은 다음 인덱스를 나타낸다
% sliderCount는 계산 결과가 이미지 배열의 범위를 벗어나지 않도록 인덱스를 조정한다.
if(btn.classList.contains("previous")) {...}
: 클릭한 버튼이 이전 버튼인지 다음 버튼인지 판별한다.
이전 버튼을 클릭한 경우 previousIndex에 해당하는 이미지를 보여주도록 moveSlider(previousIndex) 함수를 호출하며, 다음 버튼을 클릭한 경우 nextIndex에 해당하는 이미지를 보여주도록 moveSlider(nextIndex) 함수를 호출한다.