일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 코딩테스트
- HTML
- 백준골드
- 다이나믹프로그래밍
- js코테
- 프로그래머스코테
- 프로그래머스JS
- 백준구현문제
- 백준nodejs
- 익스프레스
- HTML5
- css기초
- 백준
- 백준알고리즘
- JS
- 프로그래머스
- 안드로이드 스튜디오
- 리액트
- dp알고리즘
- 백준js
- 자바스크립트
- JS프로그래머스
- 코테
- 백준구현
- 알고리즘
- CSS
- 포이마웹
- 리액트커뮤니티
- 몽고DB
- 리액트댓글기능
- Today
- Total
개발새발 로그
[2024-02-27] 팀 프로젝트 개인회고 - 뭔가 부족한 Carousel 컴포넌트 본문
1. 이전 프로젝트에서 만든 Carousel 컴포넌트 뭔가 부족하다!
먼저 내가 만든 Carousel을 보자
요소를 flex no-wrap으로 하고, overflow hidden을 적용한 다음,
scroll을 적용해서 스크롤 값으로 요소들을 드래그 할 수 있도록 했다.
근데 잘 보면 드래그를 하고 이후에 슬라이드(?) 효과가 일어난다.
이전 프로젝트에서는 생각하지 않았지만
다시 Carousel 컴포넌트를 사용하려고하니 뭔가 부족한 느낌이 들었다.
내가 원하는 건 아래와 같다.
이를 위해서 다 갈아엎기로 했다!
1. 첫번째 문제 scroll-smooth 이 녀석!
이전 프로젝트에서 scroll이 너무 부자연스러워서 넣었던 녀석인데
이 속성이 드래그했을 때 싱크가 안맞는 대신 부드럽게 하는 것이었다.
내가 원하는 드래그가 스크롤값과 싱크가 맞기 위해서는 이 속성을 빼줘야 했다.
이제 위처럼 드래그를 했을 때 스크롤값과 드래그 값이 정확히 일치하는 모습이다!
2. indicator 기능을 추가해보자!
indicator란?
특정 사상이나 현상을 대표하는 수치 내지 는 기호
아래 기능을 뜻한다.
현재 나는 scrollLeft값으로 Caroisel을 제어하고 있다,
그러면 아래와 같이 구현해야한다.
1. Carousel에서 현재 보여주고 있는 요소가 무엇인지 저장하는 state 필요
2. 만약 indicator을 클릭하면 해당되는 요소로 scroll값이 이동해야함
3. 이동할때 부드럽게 이동해야함
먼저 indicator을 현재 Carousel의 갯수에 따라 출력해주자
index.tsx
const totalSlideCarousels = Children.count(children);
...
{useNav && (
<div className="flex gap-2">
{Array.from(Array(totalSlideCarousels), (_, index) => (
<button
key={index}
onClick={() => handleClickIndicator(index)}
className={`w-[0.8rem] h-[0.8rem] ${index === currentElement ? "bg-white" : " bg-gray-400"} rounded-full`}
/>
))}
</div>
)}
그러면 이제 handleClickIndiCator과 currentElement상태를 만들어줘야한다.
useDragScroll.ts
const [currentElement, setCurrentElement] = useState(0);
...
const handleClickIndicator = (index: number) => {
if (!containerRef.current) return;
setCurrentElement(index);
containerRef.current.scrollLeft = index * childSize;
};
간단하게 만들어줬는데 문제가 있다.
scrollLeft 값을 바꾸는 로직이기 때문에 화면에서는 indicator을 눌렀을 때 순간적으로 scroll이 변경된다.
그래서 3번 조건을 충족하기위해 scrollby명령어에 behavior: "smooth"를 사용했다.
containerRef.current.scrollBy({
left: index * childSize // 이 코드는 문제가있는데 뒤에서 설명
behavior: "smooth"
});
여기서 우려되는 문제점이 있다,
iOS 모바일에서 scroll-behavior smooth 속성이 적용되지 않는다?
브라우저에서는 분명히 잘 작동이 되는데, 모바일에서는 scroll-behavior:smooth 속성이 적용되지 않는 문제가 생겼다. 모바일 스크롤 smooth를 검색해보았더니, iOS나 OSX에서의 safari에서는 Smooth Scrolling이 적용되지 않아,이를 해결하기 위해서는 자바스크립트 폴리필이 필요하다고 한다.
출처:
https://joyful-development.tistory.com/22
[조이일보:티스토리]
https://joyful-development.tistory.com/22
그래서 직접 자바스크립트 코드로 스크롤 동작을 구현하는 방법이나
polyfill을 설치해서 사용하는 방법이 있었다.
리액트에서 만약 코드를 짠다면 아래와 같은 방법이 된다.
const handleClickIndicator = (index: number) => {
if (!containerRef.current) return;
const targetScrollLeft = index * childSize;
const step = (targetScrollLeft - containerRef.current.scrollLeft) / 10;
const smoothScroll = () => {
if (containerRef.current) {
containerRef.current.scrollLeft += step;
if (Math.abs(containerRef.current.scrollLeft - targetScrollLeft) > Math.abs(step)) {
window.requestAnimationFrame(smoothScroll);
} else {
containerRef.current.scrollLeft = targetScrollLeft;
}
}
};
window.requestAnimationFrame(smoothScroll);
};
이건 예제인데 위와 같은 방법을 사용하기에는 너무 복잡해서 배제하고 진행했다.
2. 이제 요소를 드래그하고 자동으로 정렬하는 기능!
이제 중고나라의 Carousel 처럼 어느 부분을 드래그하더라도 요소가 자동 정렬이 되도록 해야한다.
먼저 조건을 확인해보자
1. 드래그 후 마우스가 놓아졌을 때 자동정렬이 된다.
2. 드래그가 끝날 때 맨 앞의 요소를 기준으로 자동 정렬이 된다.
3. 오른쪽 드래그와 왼쪽 드래그일 때 모두 자동 정렬이 가능해야한다.
먼저 드래그 후 마우스클릭이 끝났을 때는 mouseUp 이벤트를 활용하면 된다.
그리고 맨 앞의 요소를 기준을 자동 정렬을 하기 위해서는
현재 스크롤 위치에서 가장 가까운 요소의 위치를 찾아야 한다.
그니까 드래그를 만약에 했다면 마우스 드래그해서 scroll 값이 변경되고
클릭을 놓았을 때의 scroll값에서 가장 가까운 요소가 뭔지 찾는 것이다.
그럼 현재 scrollLeft값을 가져오고,
공통된 내부 요소의 너비를 활용해서 위에서 말한 요소의 인덱스 값을 찾을 수 있다.
const handleMouseUp = () => {
if (!isDragging || !containerRef.current) return;
const { scrollLeft } = containerRef.current;
const nearestElementIndex = Math.round(scrollLeft / childSize);
handleClickIndicator(nearestElementIndex);
setIsDragging(false);
};
그리고 우리가 이전에 만들어 주었던 handleClickUndicator을 재사용해서
드래그된 위치가 아니고 해당 인덱스 값으로 강제 이동하게 하는 것이다.
근데 문제가 있다.
3번 조건인 왼쪽으로 드래그 하거나 오른쪽으로 드래그 해도 가능해야한다는 점이다.
이를 위해서는 scrollBy의 Left값을 현재 scrollLeft 값에서 유동적으로 더하거나 빼줘야한다.
그럼 곰곰히 생각해보자
현재 보이는 Viewport에서 왼쪽으로 드래그했을 때 어떤 값이 필요할까?
일단 index * children은 기준이 되는 것이 맞다.
그래야지 드래그했을 때 요소가 딱 맨 앞 경계선에 맞게 위치하게 된다.
그럼 이때 생각하게 될 것이다.
왼쪽으로 드래그 했을 때는 left값이 증가하는 것이니까 plus
오른쪽으로 드래그했을 때는 left값이 감소하는 것이니까 minus
근데 무작정 plus, minus를 하면 안된다.
드래그을 놓았을 때의 위치는 항상 다른데 plus 값과 minus 값이 index * children으로 고정이면
우리가 원하는 요소가 화면에 딱 맞지 않게 된다.
그럼 여기서 필요한 값은 바로 현재 스크롤 값이다! (containerRef.current.scrollLeft)
왜 필요한지 아래 그림을 보자
그럼 오른쪽으로 드래그했을 때도 자동적으로 left에 넣어지는 값이 마이너스가 된다.
머리가 정지되어서 저 쉬운 공식을 생각하기 까지 시간이 많이걸렸다...ㅠ
물론 지금 gap 값을 포함하지 않았다.
gap 값도 포함해서 계산해야한다.
3. 만약 Carousel 내부의 요소가 하나씩 보여지지 않는 크기일 때 발생하는 문제점
이 부분은 가까운 요소를 찾아가는 로직 때문에 마지막 요소로 못가는 버그다.
그래서 마지막 요소로 못가는 모습이다.
이 부분은 사실 중고나라첢 요소가 무조건 하나만 보이는 상황만 가정했기 때문에 위 예시는 지금 만든 Carousel 컴포넌트를 사용할 때 지양하도록 해야한다.
만약 해결하려면 마지막 요소에 marginLeft 값을 요소의 반만 지정해줘서 하는 방법도 있다.
즉 드래그를 위한 빈공간이 필요한 것이다.
그래서 일단 위처럼 넣어주었다.
이 부분이 추후 문제가 될 수도 있다.
재사용성을 위해서 너무 많은 것을 부여한 것 같은 느낌이 들었지만 일단 지금 예상되는 문제가 없어서 넣어줬다.
지금 예상되는 문제는 없지만 무엇인가 문제가 될 것 같은 느낌이든다.
추후 코드도 리팩토링을 해야할 것 같다..
Carousel 컴포넌트 리팩토링에만 5시간을 쏟아냈다..ㅠㅠㅠ
'TIL' 카테고리의 다른 글
[2024-02-29] 팀 프로젝트 개인 회고 - Carousel 모바일 버그 해결, 마우스 이벤트와 터치 이벤트 순서 (0) | 2024.03.01 |
---|---|
[2024-02-28] 팀프로젝트 개인회고 - Carousel의 내부 요소가 Link면 드래그할 때 Link 작동 막기, set함수를 호출하게 두면 안돼! (0) | 2024.02.28 |
[2024-02-23] 팀 프로젝트 회고 - useModal을 만들면서, 불필요한 렌더링 (0) | 2024.02.23 |
[2024-02-20] 팀 프로젝트 개인 회고 - 다크모드 플래시 문제 해결 2(쿠키) (0) | 2024.02.20 |
[2024-02-12] 개인 프로젝트 회고 - 검색 기능 및 연관 검색어 추가 기능 구현 (1) | 2024.02.12 |