Skip to content

Commit

Permalink
feat: 캐러셀 버튼을 클릭하여 조작할 수 있도록 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
Leejha committed Oct 15, 2023
1 parent 25bcb46 commit de127fe
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 29 deletions.
157 changes: 130 additions & 27 deletions apps/jurumarble/src/app/main/components/Carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,125 @@ 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[];
}

function Carousel({ hotDrinkList }: Props) {
const router = useRouter();
const slideRef = useRef<HTMLOListElement>(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 (
<Container>
<Slides>
{hotDrinkList.map((hotDrink: GetHotDrinkResponse, index: number) => {
const { drinkId, image, name, manufactureAddress } = hotDrink;
return (
<Slide key={drinkId} onClick={() => router.push(`${Path.DRINK_INFO_PAGE}/${drinkId}`)}>
<Box>
<DrinkImageWrapper>
<RankginMark>{index + 1}</RankginMark>
<Image alt="전통주" src={image} fill style={{ borderRadius: "10px" }} />
</DrinkImageWrapper>
<DrinkText>
{name}
<AreaName>{manufactureAddress}</AreaName>
</DrinkText>
</Box>
</Slide>
);
})}
</Slides>
</Container>
<>
<Container>
<Slides ref={slideRef} isAnimation={isAnimation}>
{hotDrinkList.map((hotDrink: GetHotDrinkResponse, index: number) => {
const { drinkId, image, name, manufactureAddress } = hotDrink;
return (
<Slide
key={drinkId}
onClick={() => router.push(`${Path.DRINK_INFO_PAGE}/${drinkId}`)}
>
<Box>
<DrinkImageWrapper>
<RankginMark>{index + 1}</RankginMark>
<Image alt="전통주" src={image} fill style={{ borderRadius: "10px" }} />
</DrinkImageWrapper>
<DrinkText>
{name}
<AreaName>{manufactureAddress}</AreaName>
</DrinkText>
</Box>
</Slide>
);
})}
</Slides>
<CarouselControlContainer>
<DivideLine />
<SlideButtonContainer>
<SlideButton onClick={onPrevSlide}>
<SvgIcPrevious width={20} height={20} />
</SlideButton>
<SlideButton onClick={onNextSlide}>
<SvgNext width={20} height={20} />
</SlideButton>
</SlideButtonContainer>
</CarouselControlContainer>
</Container>
</>
);
}

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 모바일에서는 보이게 하기
**/
Expand All @@ -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;
`;

Expand Down Expand Up @@ -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;
11 changes: 9 additions & 2 deletions apps/jurumarble/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -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;

0 comments on commit de127fe

Please sign in to comment.