Skip to content

Commit

Permalink
chore: transition 방식 수정
Browse files Browse the repository at this point in the history
  • Loading branch information
jhj2713 committed Aug 17, 2024
1 parent 75507cf commit bdd05e4
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 58 deletions.
7 changes: 4 additions & 3 deletions client/src/features/CasperShowCase/CasperCards.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { useMemo } from "react";
import { CASPER_CARD_SIZE, CASPER_SIZE_OPTION } from "@/constants/CasperCustom/casper";
import { CasperCardType, TransitionCasperCards } from "./TransitionCasperCards";
import type { CasperCardType } from "@/types/casper";
import { TransitionCasperCards } from "./TransitionCasperCards";

interface CasperCardsProps {
cardList: CasperCardType[];
}

export function CasperCards({ cardList }: CasperCardsProps) {
const cardLength = cardList.length;
const cardLength = 20;
const cardLengthHalf = Math.floor(cardLength / 2);
const visibleCardCount = useMemo(() => {
const width = window.innerWidth;
Expand All @@ -22,7 +23,7 @@ export function CasperCards({ cardList }: CasperCardsProps) {

const itemWidth = CASPER_CARD_SIZE[CASPER_SIZE_OPTION.SM].CARD_WIDTH;
const gap = 40;
const totalWidth = (itemWidth + gap) * topCardList.length;
const totalWidth = (itemWidth + gap) * visibleCardCount;

const isEndTopCard = (latestX: number) => {
return latestX <= -totalWidth;
Expand Down
53 changes: 53 additions & 0 deletions client/src/features/CasperShowCase/TransitionCasperCardItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { useState } from "react";
import { CASPER_CARD_SIZE, CASPER_SIZE_OPTION } from "@/constants/CasperCustom/casper";
import useLazyLoading from "@/hooks/useLazyLoading";
import type { CasperCardType } from "@/types/casper";
import { CasperFlipCard } from "../CasperCustom/CasperFlipCard";

interface TransitionCasperCardItemProps {
cardItem: CasperCardType;
id: string;
stopAnimation?: () => void;
startAnimation?: () => void;
}

export function TransitionCasperCardItem({
cardItem,
id,
stopAnimation,
startAnimation,
}: TransitionCasperCardItemProps) {
const [isFlipped, setIsFlipped] = useState<boolean>(false);
const { isInView, cardRef } = useLazyLoading<HTMLLIElement>();

const handleMouseEnter = () => {
stopAnimation && stopAnimation();
setIsFlipped(true);
};

const handleMouseLeave = () => {
startAnimation && startAnimation();
setIsFlipped(false);
};

return (
<li
ref={cardRef}
key={id}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
style={{
width: CASPER_CARD_SIZE[CASPER_SIZE_OPTION.SM].CARD_WIDTH,
height: CASPER_CARD_SIZE[CASPER_SIZE_OPTION.SM].CARD_HEIGHT,
}}
>
{isInView && (
<CasperFlipCard
card={cardItem}
size={CASPER_SIZE_OPTION.SM}
isFlipped={isFlipped}
/>
)}
</li>
);
}
103 changes: 48 additions & 55 deletions client/src/features/CasperShowCase/TransitionCasperCards.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
import { useEffect, useMemo, useRef, useState } from "react";
import { AnimatePresence, motion, useAnimation } from "framer-motion";
import { AnimatePresence, type ResolvedValues, motion, useAnimation } from "framer-motion";
import { CASPER_CARD_SIZE, CASPER_SIZE_OPTION } from "@/constants/CasperCustom/casper";
import { CARD_TRANSITION } from "@/constants/CasperShowCase/showCase";
import useLazyLoading from "@/hooks/useLazyLoading";
import { SelectedCasperIdxType } from "@/types/casperCustom";
import { CasperFlipCard } from "../CasperCustom/CasperFlipCard";
import { CasperCardType } from "@/types/casper";
import { TransitionCasperCardItem } from "./TransitionCasperCardItem";

export interface CasperCardType {
id: number;
casperName: string;
expectations: string;
selectedCasperIdx: SelectedCasperIdxType;
}
interface TransitionCasperCardsProps {
cardList: CasperCardType[];
initialX: number;
Expand All @@ -36,11 +29,13 @@ export function TransitionCasperCards({
return Math.ceil(width / cardWidth);
}, []);
const isAnimated = visibleCardCount <= cardList.length;
const expandedCardList = useMemo(() => [...cardList, ...cardList], [cardList]);

const containerRef = useRef<HTMLUListElement>(null);
const transitionControls = useAnimation();

const [x, setX] = useState<number>(initialX);
const [visibleCardListIdx, setVisibleCardListIdx] = useState(0);

const startAnimation = (x: number) => {
transitionControls.start({
Expand All @@ -58,52 +53,40 @@ export function TransitionCasperCards({
}
};

useEffect(() => {
startAnimation(x);
}, [transitionControls, totalWidth]);

const expandedCardList = useMemo(() => {
const visibleCardList = useMemo(() => {
if (isAnimated) {
return [...cardList, ...cardList.slice(0, visibleCardCount)];
return [
...expandedCardList.slice(
visibleCardListIdx,
visibleCardListIdx + visibleCardCount
),
...expandedCardList.slice(
visibleCardListIdx + visibleCardCount,
visibleCardListIdx + visibleCardCount * 2
),
];
}

return cardList;
}, [cardList]);
}, [cardList, visibleCardCount, visibleCardListIdx]);

const renderCardItem = (cardItem: CasperCardType, id: string) => {
const [isFlipped, setIsFlipped] = useState<boolean>(false);
const { isInView, cardRef } = useLazyLoading<HTMLLIElement>();
useEffect(() => {
startAnimation(x);
}, [transitionControls, totalWidth]);

const handleMouseEnter = () => {
stopAnimation();
setIsFlipped(true);
};
const handleUpdateAnimation = (latest: ResolvedValues) => {
if (isEndCard(parseInt(String(latest.x)))) {
startAnimation(initialX);

const handleMouseLeave = () => {
startAnimation(x);
setIsFlipped(false);
};
let nextIdx = visibleCardListIdx + visibleCardCount;

return (
<li
ref={cardRef}
key={id}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
style={{
width: CASPER_CARD_SIZE[CASPER_SIZE_OPTION.SM].CARD_WIDTH,
height: CASPER_CARD_SIZE[CASPER_SIZE_OPTION.SM].CARD_HEIGHT,
}}
>
{isInView && (
<CasperFlipCard
card={cardItem}
size={CASPER_SIZE_OPTION.SM}
isFlipped={isFlipped}
/>
)}
</li>
);
// 만약 nextIdx가 cardList의 길이를 초과하면 0으로 초기화하거나 초과분을 조정합니다.
if (nextIdx + visibleCardCount >= cardList.length) {
nextIdx = (nextIdx + visibleCardCount) % cardList.length;
}

setVisibleCardListIdx(nextIdx);
}
};

return (
Expand All @@ -114,17 +97,27 @@ export function TransitionCasperCards({
className="flex"
animate={transitionControls}
style={{ gap: `${gap}px` }}
onUpdate={(latest) => {
if (isEndCard(parseInt(String(latest.x)))) {
startAnimation(initialX);
}
}}
onUpdate={handleUpdateAnimation}
>
{expandedCardList.map((card, idx) => renderCardItem(card, `${card.id}-${idx}`))}
{visibleCardList.map((card, idx) => (
<TransitionCasperCardItem
key={`${card.id}-${idx}`}
cardItem={card}
id={`${card.id}-${idx}`}
stopAnimation={stopAnimation}
startAnimation={() => startAnimation(x)}
/>
))}
</motion.ul>
) : (
<ul className="flex w-screen justify-center" style={{ gap: `${gap}px` }}>
{expandedCardList.map((card, idx) => renderCardItem(card, `${card.id}-${idx}`))}
{visibleCardList.map((card, idx) => (
<TransitionCasperCardItem
key={`${card.id}-${idx}`}
cardItem={card}
id={`${card.id}-${idx}`}
/>
))}
</ul>
)}
</AnimatePresence>
Expand Down
8 changes: 8 additions & 0 deletions client/src/types/casper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { SelectedCasperIdxType } from "./casperCustom";

export interface CasperCardType {
id: number;
casperName: string;
expectations: string;
selectedCasperIdx: SelectedCasperIdxType;
}

0 comments on commit bdd05e4

Please sign in to comment.