diff --git a/apps/jurumarble/src/app/main/components/Carousel.tsx b/apps/jurumarble/src/app/main/components/Carousel.tsx index db0286e0..c9350f90 100644 --- a/apps/jurumarble/src/app/main/components/Carousel.tsx +++ b/apps/jurumarble/src/app/main/components/Carousel.tsx @@ -3,6 +3,12 @@ import Path from "lib/Path"; import Image from "next/image"; import { useRouter } from "next/navigation"; import styled, { css } from "styled-components"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { SvgIcPrevious, SvgNext } from "src/assets/icons/components"; + +const SLIDE_MOVE_COUNT = 1; +const ORIGINAL_IMAGE_LENGTH = 10; +const MOVE_DISTANCE = 300; interface Props { hotDrinkList: GetHotDrinkResponse[]; @@ -10,42 +16,112 @@ interface Props { function Carousel({ hotDrinkList }: Props) { const router = useRouter(); + const slideRef = useRef(null); + const [currentSlide, setCurrentSlide] = useState(1); + const [isAnimation, setIsAnimation] = useState(true); + const [isFlowing, setIsFlowing] = useState(true); + + const onNextSlide = useCallback(() => { + setCurrentSlide((prev) => prev + SLIDE_MOVE_COUNT); + }, [currentSlide]); + + const onPrevSlide = useCallback(() => { + setCurrentSlide((prev) => prev - SLIDE_MOVE_COUNT); + }, [currentSlide]); + + useEffect(() => { + if (!slideRef.current) return; + + if (currentSlide === ORIGINAL_IMAGE_LENGTH + 1) { + setTimeout(() => { + setIsAnimation(false); + slideRef.current!.style.transform = `translateX(-${ + MOVE_DISTANCE * ORIGINAL_IMAGE_LENGTH + }px)`; + setCurrentSlide(1); + }, 500); + + setTimeout(() => { + setIsAnimation(true); + }, 600); + } else if (currentSlide === 0) { + setTimeout(() => { + setIsAnimation(false); + slideRef.current!.style.transform = `translateX(+${MOVE_DISTANCE}px)`; + setCurrentSlide(ORIGINAL_IMAGE_LENGTH); + }, 500); + + setTimeout(() => { + setIsAnimation(true); + }, 600); + } + slideRef.current.style.transform = `translateX(-${MOVE_DISTANCE * (currentSlide - 1)}px)`; + }, [currentSlide]); + + useEffect(() => { + let intervalId: NodeJS.Timeout; + if (isFlowing) { + intervalId = setInterval(() => { + setCurrentSlide((prev) => prev + SLIDE_MOVE_COUNT); + }, 3500); + } + return () => clearTimeout(intervalId); + }, [isFlowing, currentSlide]); + return ( - - - {hotDrinkList.map((hotDrink: GetHotDrinkResponse, index: number) => { - const { drinkId, image, name, manufactureAddress } = hotDrink; - return ( - router.push(`${Path.DRINK_INFO_PAGE}/${drinkId}`)}> - - - {index + 1} - 전통주 - - - {name} - {manufactureAddress} - - - - ); - })} - - + <> + + + {hotDrinkList.map((hotDrink: GetHotDrinkResponse, index: number) => { + const { drinkId, image, name, manufactureAddress } = hotDrink; + return ( + router.push(`${Path.DRINK_INFO_PAGE}/${drinkId}`)} + > + + + {index + 1} + 전통주 + + + {name} + {manufactureAddress} + + + + ); + })} + + + + + + + + + + + + + + ); } const Container = styled.div` - height: 188px; margin-top: 32px; + overflow: hidden; `; -const Slides = styled.ol` +const Slides = styled.ol<{ isAnimation: boolean }>` display: flex; - height: 168px; - overflow-x: auto; - scroll-snap-type: x mandatory; + /* overflow-x: auto; + scroll-snap-type: x mandatory; */ gap: 8px; + transition: transform 0.5s ease-in-out; + ${({ isAnimation }) => isAnimation && `transform: translateX(-${MOVE_DISTANCE}px);`} + /** @Todo 모바일에서는 보이게 하기 **/ @@ -60,7 +136,7 @@ const Slide = styled.li` width: 292px; height: 120px; padding-top: 20px; - scroll-snap-align: start; + /* scroll-snap-align: start; */ cursor: pointer; `; @@ -120,4 +196,31 @@ const AreaName = styled.span` `} `; +const CarouselControlContainer = styled.div` + margin-top: 28px; + display: flex; + align-items: center; + gap: 20px; +`; + +const DivideLine = styled.div` + ${({ theme }) => css` + background-color: ${theme.colors.line_01}; + height: 2px; + width: 100%; + `} +`; + +const SlideButtonContainer = styled.div` + display: flex; + gap: 10px; +`; + +const SlideButton = styled.button` + border-radius: 100px; + border: 1px solid ${({ theme }) => theme.colors.line_01}; + width: 40px; + height: 40px; +`; + export default Carousel; diff --git a/apps/jurumarble/src/app/page.tsx b/apps/jurumarble/src/app/page.tsx index d651d314..c2239b55 100644 --- a/apps/jurumarble/src/app/page.tsx +++ b/apps/jurumarble/src/app/page.tsx @@ -1,9 +1,8 @@ "use client"; import BottomBar from "components/BottomBar"; -import DivideLine from "components/DivideLine"; import Header from "components/Header"; -import styled from "styled-components"; +import styled, { css } from "styled-components"; import Banner from "./main/components/Banner"; import HotDrinkContainer from "./main/components/HotDrinkContainer"; import HotDrinkVoteContainer from "./main/components/HotDrinkVoteContainer"; @@ -39,4 +38,12 @@ const BottomSection = styled.section` overflow: auto; `; +const DivideLine = styled.div` + ${({ theme }) => css` + background-color: ${theme.colors.bg_01}; + height: 8px; + margin: 40px 0 8px 0; + `} +`; + export default MainPage;