diff --git a/components/Home/Activity/Carousel.module.scss b/components/Home/Activity/Carousel.module.scss index 47ea21f..dafac46 100644 --- a/components/Home/Activity/Carousel.module.scss +++ b/components/Home/Activity/Carousel.module.scss @@ -21,6 +21,7 @@ $carousel-length: 5; gap: 5vw; overflow-x: scroll; scroll-snap-type: x mandatory; + cursor: grab; // 처음과 마지막 캐러셀 콘텐츠의 가운데 정렬을 맞추기 위한 가상요소 &::before { @@ -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; } diff --git a/components/Home/Activity/Carousel.tsx b/components/Home/Activity/Carousel.tsx index c7f62e3..6dec282 100644 --- a/components/Home/Activity/Carousel.tsx +++ b/components/Home/Activity/Carousel.tsx @@ -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"; @@ -16,14 +16,52 @@ export default function Carousel({ selectedId, setSelectedId, }: CarouselProps) { + const carouselRef = useRef(null); + const scrollSnapTimeoutRef = useRef(null); + const carouselScrollHandler = useCallback( (e: React.MouseEvent) => { + console.log(e.type); const stride = window.innerWidth * 0.85; setSelectedId(Math.round(e.currentTarget.scrollLeft / stride)); }, - [], + [setSelectedId], ); + const carouselMouseDownHandler = (e: React.MouseEvent) => { + e.currentTarget.style.cursor = "grabbing"; + e.currentTarget.style.scrollSnapType = "none"; + if (scrollSnapTimeoutRef.current) { + clearTimeout(scrollSnapTimeoutRef.current); + } + }; + + const carouselMouseMoveHandler = (e: React.MouseEvent) => { + if (e.currentTarget.style.cursor === "grabbing") { + e.currentTarget.scrollLeft -= e.movementX; + } + }; + + const carouselMouseUpHandler = (e: React.MouseEvent) => { + 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 ( <>
@@ -31,6 +69,11 @@ export default function Carousel({
{activities.map((activity, id) => ( @@ -40,10 +83,11 @@ export default function Carousel({
    - {[0, 1, 2, 3, 4].map((id) => ( + {activities.map((activity, id) => (
    ))}
@@ -59,7 +103,7 @@ function CarouselItem({ activity }: CarouselItemProps) { return (
- {activity.altText} + {activity.altText}

{activity.head}