
자바스크립트로 carousel(캐러셀)구조를 구현하는 토이프로젝트를 진행해보았다.
캐러셀 구조로 html 문서 작성하기
css 스타일시트를 이용해 해당 이미지들의 노출 상태+위치 작성하기
버튼을 눌렀을 때 요소에 클래스가 추가되도록 코드 작성하기
<div class="wrap">
| <div class="device">
| | <div class="decoration"></div>
| | <div class="carousel-wrapper">
| | | <div class="carousel">
| | | <img class="carousel_item" src="assets/1.jpeg" />
| | | <img class="carousel_item" src="assets/2.jpeg" />
| | | <img class="carousel_item" src="assets/3.jpeg" />
| | | <img class="carousel_item" src="assets/4.jpeg" />
| | | <img class="carousel_item" src="assets/5.jpeg" />
| | |
| | | <div class="buttons">
| | | <div class="carousel_button--next"></div>
| | | <div class="carousel_button--prev"></div>
| | | </div>
| | | </div>
| | </div>
| | <div class="decoration"></div>
| </div>
</div>
(코드의 '|'의 경우 가독성을 위해 추가한 꾸밈요소입니다)
우선 캐러셀 기본 구조를 이렇게 작성했다.
요소를 화면 정 가운데에 위치시키기 위해 display flex를 이용할 생각이었고,
container요소를 wrap 클래스명을 가진 div태그로 만들었다.
메인이 되는 carousel-wrapper 클래스 div의 내부에 carousel영역과 button영역을 만들어 각각 위의 코드처럼 요소들을 담아주었다.
.carousel-wrapper {
overflow: hidden;
width: 90%;
height: 100%;
margin: auto;
}
.carousel-wrapper * {
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
}
.carousel {
position: relative;
top: 50px;
display: flex;
width: 100%;
height: 75%;
}
.carousel_item {
position: absolute;
max-height: 500px;
opacity: 0;
transition: transform 0.5s, opacity 0.5s, z-index 0.5s;
}
.carousel_item.active {
opacity: 1;
z-index: 900;
}
.carousel_item.prev,
.carousel_item.next {
z-index: 800;
}
.carousel_item.prev {
transform: translateX(-100%);
}
.carousel_item.next {
transform: translateX(100%);
}
우선 각각의 아이템들이 줄줄이 다른 곳에 위치해 있을 경우, transform으로 위치를 변경해줄 때 번거로우므로 position absolute를 통해 모두 한 위치에 모이도록 해 주고, active될 때 빼고는 보이지 않도록 opacity 값을 0으로 지정해준다.
그리고 active됐을 때는 그 위치에서 opacity 값을 1로 해서 보이도록 해 주고, z-index도 제일 위로 올 수 있도록 설정해준다.
prev와 next가 될 요소들은 다른 요소들보다는 위에 있고, active보다는 아래에 위치하도록 z-index 값을 준다. 이렇게 하면 z-index값의 변화로 active가 되면서 위로 올라오는 듯한 느낌을 줄 수 있다. 옆에서 등장하는 transform이 발생할 수 있도록 미리 active요소의 좌우 위치에 위치시킨다.
'use strict'
const get = (target) => {
return document.querySelector(target)
}
// 캐러셀 클래스를 만들어보자
class Carousel {
constructor(carouselElement) {
this.carouselElement = carouselElement
this.itemClassName = 'carousel_item'
this.items = this.carouselElement.querySelectorAll('.carousel_item')
this.totalItems = this.items.length
this.current = 0
this.isMoving = false
}
}
우선 'use strict'를 이용해 엄격모드로 진행한다.
그리고 querySelector를 이용한 유틸성 함수인 get( )을 선언해주었다.
carousel 기능은 class를 이용해 만들어 줄 예정이므로 Carousel class를 생성했다.
생성자 함수로 carousel요소돔을 받을 예정이다. 생성자 함수 내부에는 기능 구현을 위한 변수들을 선언해준다.
// 버튼에 이벤트 등록
setEventListener() {
this.prevButton = this.carouselElement.querySelector(
'.carousel_button--prev'
)
this.nextButton = this.carouselElement.querySelector(
'.carousel_button--next'
)
this.prevButton.addEventListener('click', () => {
this.movePrev()
})
this.nextButton.addEventListener('click', () => {
this.moveNext()
})
}
moveNext() {
if (this.isMoving) return
if (this.current === this.totalItems - 1) {
// current가 총아이템개수 - 1일 때(마지막요소)
this.current = 0 // moveNext()실행 시, current를 첫 요소로 설정해주기 위해
} else {
this.current++
}
this.moveCarouselTo()
}
movePrev() {
if (this.isMoving) return
if (this.current === 0) {
this.current = this.totalItems - 1
} else {
this.current--
}
this.moveCarouselTo()
}
우선 버튼 요소에 이벤트를 등록하는 setEventListener( )함수를 선언하고 prev버튼과 next버튼의 click이벤트에 movePrev( )함수와 moveNext( )함수를 등록해주었다.
움직임을 위해 현재 요소를 업데이트 해줄 함수들을 작성한다.
움직이는 상태일 경우에는 동작하지 않도록 해주고, 다음으로 움직일 때는 마지막 요소일 때만 예외처리로 첫 요소로 움직일 수 있도록 current를 0으로 변경해주고, 나머지는 current값을 +1해준다. 반대로 이전으로 움직일 때는 첫 요소일 때만 예외처리로 마지막요소로 움직일 수 있도록 current를 설정해주고, 나머지는 current값을 -1해준다.
그리고 moveCarouselTo( )함수를 따로 작성해 각각에 알맞은 클래스를 넣어 transform하게 만들어줄 예정이다.
// 버튼을 연속으로 눌렀을 때, 모든 이벤트에 반응하지 않고 지연되게 하기 위해
disabledInteraction() {
this.isMoving = true
setTimeout(() => {
this.isMoving = false
}, 500)
}
// 캐러셀 동작 함수
moveCarouselTo() {
if (this.isMoving) return
this.disabledInteraction() // 캐러셀이 움직이기 시작하면 버튼지연동작
// prev와 next값 정의
let prev = this.current - 1
let next = this.current + 1
if (this.current === 0) {
prev = this.totalItems - 1
} else if (this.current === this.totalItems - 1) {
next = 0
}
// 아이템들에 맞는 클래스명을 붙여주기
for (let i = 0; i < this.totalItems; i++) {
if (i === this.current) {
this.items[i].className = this.itemClassName + ' active' // class명을 이렇게 붙여줄 수도 있음
} else if (i === prev) {
this.items[i].className = this.itemClassName + ' prev'
} else if (i === next) {
this.items[i].className = this.itemClassName + ' next'
} else {
this.items[i].className = this.itemClassName
}
}
}
캐러셀의 동작은 움직이는 중에 실행되면 안되므로 isMoving이 true일 때는 동작하지 않도록 했고, 이벤트 제어 방법으로 스로틀 방식을 이용해 연속으로 빠르게 여러번 클릭했을 때, 매 클릭마다 움직이지는 않도록 할 것이다. 이 역할을 해줄 함수 disabledInteraction( )함수를 작성해주었다.
이 함수를 캐러셀 동작 함수가 호출되면 가장 먼저 실행될 수 있도록 해주고, prev와 next에 현재 캐러셀 요소를 의미하는 current에 -1, +1을 하여 할당해준다.
그리고 만약 current가 0이면 가장 첫 요소이므로 이전요소가 마지막 요소일 수 있도록 해주고, current가 마지막요소일 경우, 다음요소가 첫 요소일 수 있도록 해준다.
반복문을 통해 이 캐러셀 아이템 요소들에 적절한 클래스명을 붙여 동작하도록 할 것이다.
<요소들의 값> 아래와같이 연결될 수 있도록
0
4 1
3 2
// 캐러셀 초기화 - 처음아이템을 current로 설정
initCarousel() {
this.isMoving = false
this.items[0].classList.add('active')
this.items[1].classList.add('next')
this.items[this.totalItems - 1].classList.add('prev')
}
항상 새로고침할 때마다 똑같이 작동하고, 첫번째 아이템먼저 노출될 수 있도록 초기화 함수를 만들어준다.
document.addEventListener('DOMContentLoaded', () => {
const carouselElement = get('.carousel')
const carousel = new Carousel(carouselElement)
carousel.initCarousel() // 캐러셀 초기화
carousel.setEventListener() // 각 버튼에 이벤트 등록
})
모든 요소가 load되면 실행될 수 있도록 'DOMContentLoaded'에 carousel클래스를 가진 요소를 불러와 Carousel 클래스객체를 생성해주고 함수를 호출해준다.
코드들을 즉시실행 함수로 감싸 변수충돌을 방지해준다.