Skip to content

Commit

Permalink
Feat/ 홈 탭 캐러셀 개선 (#54)
Browse files Browse the repository at this point in the history
* fix: 캐러셀 인디케이터 못생기게 변하는 거

* feat: 인디케이터 클릭하면 캐러셀 이동

* feat: 마우스 드래그 시 좌우 스크롤

* fix: 드래그를 연속적으로 할 때 원치 않을 때 scroll-snap이 적용되는 버그
  • Loading branch information
minkyu97 authored Nov 26, 2023
1 parent 5a3cce9 commit 7b4ba1a
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 5 deletions.
3 changes: 2 additions & 1 deletion components/Home/Activity/Carousel.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ $carousel-length: 5;
gap: 5vw;
overflow-x: scroll;
scroll-snap-type: x mandatory;
cursor: grab;

// 처음과 마지막 캐러셀 콘텐츠의 가운데 정렬을 맞추기 위한 가상요소
&::before {
Expand Down Expand Up @@ -94,7 +95,7 @@ $carousel-length: 5;
.dot {
width: 0.8em;
height: 0.8em;
border-radius: 100%;
border-radius: 0.4em;
background-color: #e7e7e7;
transition: 0.2s;
}
Expand Down
52 changes: 48 additions & 4 deletions components/Home/Activity/Carousel.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import classNames from "classnames/bind";
import { useCallback, useMemo } from "react";
import { useCallback, useRef } from "react";
import styles from "./Carousel.module.scss";
import { ActivityData } from "./ActivityData";

Expand All @@ -16,21 +16,64 @@ export default function Carousel({
selectedId,
setSelectedId,
}: CarouselProps) {
const carouselRef = useRef<HTMLDivElement>(null);
const scrollSnapTimeoutRef = useRef<NodeJS.Timeout | null>(null);

const carouselScrollHandler = useCallback(
(e: React.MouseEvent<HTMLDivElement>) => {
console.log(e.type);
const stride = window.innerWidth * 0.85;
setSelectedId(Math.round(e.currentTarget.scrollLeft / stride));
},
[],
[setSelectedId],
);

const carouselMouseDownHandler = (e: React.MouseEvent<HTMLDivElement>) => {
e.currentTarget.style.cursor = "grabbing";
e.currentTarget.style.scrollSnapType = "none";
if (scrollSnapTimeoutRef.current) {
clearTimeout(scrollSnapTimeoutRef.current);
}
};

const carouselMouseMoveHandler = (e: React.MouseEvent<HTMLDivElement>) => {
if (e.currentTarget.style.cursor === "grabbing") {
e.currentTarget.scrollLeft -= e.movementX;
}
};

const carouselMouseUpHandler = (e: React.MouseEvent<HTMLDivElement>) => {
e.currentTarget.style.cursor = "grab";
const stride = window.innerWidth * 0.85;
e.currentTarget.scrollTo({
left: selectedId * stride,
behavior: "smooth",
});
const target = e.currentTarget;
scrollSnapTimeoutRef.current = setTimeout(() => {
target.style.scrollSnapType = "x mandatory";
}, 400);
};

const indicatorClickHandler = (id: number) => () => {
carouselRef.current?.scrollTo({
left: (id * carouselRef.current.scrollWidth) / activities.length,
behavior: "smooth",
});
};

return (
<>
<div className={cx("carousel")}>
<div className={cx("carouselFrame")}>
<div
className={cx("carouselItemsContainer")}
onScroll={carouselScrollHandler}
onMouseDown={carouselMouseDownHandler}
onMouseMove={carouselMouseMoveHandler}
onMouseUp={carouselMouseUpHandler}
ref={carouselRef}
draggable={false}
>
{activities.map((activity, id) => (
<CarouselItem key={id} activity={activity} />
Expand All @@ -40,10 +83,11 @@ export default function Carousel({
</div>

<ul className={cx("carouselIndicator")}>
{[0, 1, 2, 3, 4].map((id) => (
{activities.map((activity, id) => (
<div
key={id}
className={cx("dot", id === selectedId ? "selected" : "")}
onClick={indicatorClickHandler(id)}
/>
))}
</ul>
Expand All @@ -59,7 +103,7 @@ function CarouselItem({ activity }: CarouselItemProps) {
return (
<div className={cx("carouselItem")}>
<div className={cx("imageContainer")}>
<img src={activity.image} alt={activity.altText} />
<img src={activity.image} alt={activity.altText} draggable={false} />
</div>
<div className={cx("textContainer")}>
<h1 className={cx("activityTitle")}>{activity.head}</h1>
Expand Down

0 comments on commit 7b4ba1a

Please sign in to comment.