잘 설명한 분들이 많으므로 원리는 아래의 블로그, 코드는 유튜브 영상을 참고하길.
근데 이 유튜브 영상을 그대로 따라해도 문제가 발생할 것이다. 그 때 이 글을 참고하라고 이렇게 적어본다.
결과물과 코드는 github에 올려뒀다.
https://github.com/bluepebble25/vanillaJS-UI/tree/main/carousel-slider
여길 참고해서 만들기
- 코드 - https://www.youtube.com/watch?v=KcdBOoK3Pfw&t=941s
- 원리1 - https://velog.io/@soyeon9819/React%EC%BA%90%EB%9F%AC%EC%85%80-%EB%A7%8C%EB%93%A4%EA%B8%B01-%EB%AC%B4%ED%95%9C-%EC%BA%90%EB%9F%AC%EC%85%80
- 원리2 - https://im-developer.tistory.com/97
처음과 마지막 슬라이드를 앞 뒤에 중복 삽입한 이유
1 2 3 4 5
의 앞 뒤에 맨 끝과 첫번째 이미지와 같은 이미지를 붙여넣어 5 1 2 3 4 5 1
로 만든다.
현재 5번째 차례에 있다고 치면 오른쪽 버튼을 누르면 1번으로 이동할 때 오른쪽으로 부드럽게 넘어간 것처럼 보인다.
현재 위치가 5 1 2 3 4 5 "1"
에 온 찰나의 순간 5 "1" 2 3 4 5 1
이 1번으로 위치를 바꿔버리면 보는 사람은 맨 처음 1번으로 이동한 줄 모르게 된다.
만들면서 겪었던 문제
1. clientWidth가 자꾸 0으로 나오다
이미지의 사이즈만큼 슬라이드를 옆으로 이동시키기 위해 이미지 사이즈를 구했다. 그런데 자꾸만 0이 나왔다.
const IMAGE_SIZE = carouselImages[0].clientWidth;
이유를 알아보기 위해 clientWidth에 대해 검색해보니 크기가 지정되어 있지 않거나 inline인 요소의 크기는 구할 수 없다고 나왔다. 그래서 block으로 지정해보기도 하고 크기를 고정크기(px)로 주기도 해봤는데도 여전히 0이 나왔다.
원인
이미지 로딩이 완료되지 않은 상태에서 크기를 구하려고 했기 때문이다. js 파일을 로딩할 때 defer를 줘서 DOM이 모두 생성된 다음에 크기를 구하기 때문에 괜찮을 거라 생각했는데, DOM 생성 완료와 이미지 로딩은 별도이다. 마치 액자의 프레임(DOM)은 생성되었지만 그 안의 그림이 아직 생기지 않아, 액자가 아니라 그림으로 크기를 구하는 clientWidth가 크기를 구할 수 없었던 것이다.
해결
js 파일 전체를 window.onload = function() { code }
로 감싸는 걸로 해결했다. 이러면 이미지 로드까지 완료됐을 때 js 코드를 실행하게 된다.
아마도 이를 onload가 아니라 promise로 해결할 수도 있을 것이다.
2. 버튼 클릭을 연타하면 처음으로 돌아가지 않고 흰 배경이 보이다
참고한 영상에서는 transitionend
이벤트가 발생할 때마다(transition이 end될 때) 현재 위치가 어디인지 검사하고, 만약 마지막 슬라이드(1번 슬라이드의 복제)에 도착했다면 transition을 잠시 none으로 바꾸고 진짜 1번 슬라이드로 점프하게 구현했다. 하지만 이러면 사용자가 아주 빠르게 화살표를 연타할 경우 transitionend 이벤트가 감지되기도 전에 다음 칸으로 넘어가서 흰 배경만 보이게 되고 그 상태에서 계속 옆으로만 넘어가 흰 배경 위를 계속 이동하는 상황이 발생한다.
이런 사태가 일어나지 않도록 내가 구현한 방법은 이렇다.
transitionend 이벤트는 transition이 멈춘 찰나의 순간을 포착해 슬라이드를 점프하기 위함이다. 그 방법을 사용하지 않고 마지막 슬라이드(1번의 복제)에 도달하면 setTimeout으로 0.4초 뒤에 transition을 none으로 설정한 다음 진짜 1번으로 점프, 바로 2번으로 슬라이드 되게 했다. 그러면 보는 사람에게는 0.4초 뒤에 바로 2번으로 슬라이드된 것 처럼 보이게 된다.
function next() {
carouselSlide.style.transition = `transform ${TRANSITION_TIME}s ease-in-out`;
counter++;
carouselSlide.style.transform = `translateX(${-IMAGE_SIZE * counter}px)`;
if(counter == carouselImages.length - 1) {
setTimeout(() => {
carouselSlide.style.transition = 'none';
counter = 1;
carouselSlide.style.transform = `translateX(${-IMAGE_SIZE * counter}px)`;
}, 400);
}
}
내가 추가해 본 기능
.carousel-container
에.autoSlide
클래스를 추가하면 setInterval()을 통해 일정 시간마다 스스로 슬라이드를 넘기도록 했다.
앞으로 추가할 기능
- 동그라미 아이콘으로 현재 위치 확인, 동그라미 클릭하면 해당 페이지로 이동
- setIinterval()과 직접 이동하기 버튼 클릭이 충돌해서 어떤 슬라이드는 너무 빠르게 넘어가는 현상 수정
- 클래스를 통해 옵션 조정할 수 있게 하기 (transition 시간)
'UI 구현 연구일지' 카테고리의 다른 글
[React] Debounce(디바운스) 적용한 검색어 자동완성 구현기 (1) (0) | 2023.06.14 |
---|---|
[React 컴포넌트] 댓글 기능 구현하기 (0) | 2022.07.12 |
[React 컴포넌트] Like, Dislike (좋아요, 싫어요) 기능 만드는 법 (0) | 2022.07.11 |
[React 컴포넌트] Favorite(찜, 좋아요) 버튼 만드는 법 (0) | 2022.07.11 |
[React 컴포넌트] 더 보기(Load More) 버튼 만드는 법 (0) | 2022.07.08 |