diff --git a/client/src/features/CasperCustom/CasperFlipCard.tsx b/client/src/features/CasperCustom/CasperFlipCard.tsx index e89d04df..4b7f23b7 100644 --- a/client/src/features/CasperCustom/CasperFlipCard.tsx +++ b/client/src/features/CasperCustom/CasperFlipCard.tsx @@ -1,3 +1,4 @@ +import { memo } from "react"; import { motion } from "framer-motion"; import { CASPER_CARD_SIZE, CASPER_SIZE_OPTION } from "@/constants/CasperCustom/casper"; import { CasperCardType } from "../CasperShowCase/TransitionCasperCards"; @@ -10,7 +11,7 @@ interface CasperFlipCardProps { isFlipped: boolean; } -export default function CasperFlipCard({ size, card, isFlipped }: CasperFlipCardProps) { +function CasperFlipCard({ size, card, isFlipped }: CasperFlipCardProps) { return (
); } + +export default memo(CasperFlipCard); diff --git a/client/src/features/CasperShowCase/TransitionCasperCards.tsx b/client/src/features/CasperShowCase/TransitionCasperCards.tsx index 294cd41c..4a0ee465 100644 --- a/client/src/features/CasperShowCase/TransitionCasperCards.tsx +++ b/client/src/features/CasperShowCase/TransitionCasperCards.tsx @@ -1,7 +1,8 @@ import { useEffect, useRef, useState } from "react"; import { motion, useAnimation } from "framer-motion"; -import { CASPER_SIZE_OPTION } from "@/constants/CasperCustom/casper"; +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"; @@ -28,7 +29,7 @@ export default function TransitionCasperCards({ gap, isEndCard, }: TransitionCasperCardsProps) { - const containerRef = useRef(null); + const containerRef = useRef(null); const transitionControls = useAnimation(); const [x, setX] = useState(initialX); @@ -55,6 +56,7 @@ export default function TransitionCasperCards({ const renderCardItem = (cardItem: CasperCardType, id: string) => { const [isFlipped, setIsFlipped] = useState(false); + const { isInView, cardRef } = useLazyLoading(); const handleMouseEnter = () => { stopAnimation(); @@ -67,18 +69,29 @@ export default function TransitionCasperCards({ }; return ( -
- -
+
  • + {isInView && ( + + )} +
  • ); }; return ( - {cardList.map((card) => renderCardItem(card, card.id))} {cardList.map((card) => renderCardItem(card, `${card.id}-clone`))} - + ); } diff --git a/client/src/hooks/useLazyLoading.ts b/client/src/hooks/useLazyLoading.ts new file mode 100644 index 00000000..6d612e41 --- /dev/null +++ b/client/src/hooks/useLazyLoading.ts @@ -0,0 +1,30 @@ +import { useEffect, useRef, useState } from "react"; + +export default function useLazyLoading() { + const [isInView, setIsInView] = useState(false); + const cardRef = useRef(null); + + useEffect(() => { + const observer = new IntersectionObserver( + ([entry]) => { + if (entry.isIntersecting) { + setIsInView(true); + observer.disconnect(); + } + }, + { threshold: 0, root: document.body } + ); + + if (cardRef.current) { + observer.observe(cardRef.current); + } + + return () => { + if (cardRef.current) { + observer.unobserve(cardRef.current); + } + }; + }, [cardRef]); + + return { isInView, cardRef }; +}