From 94e866e67b9bdd1e1cc1c84b51fc0a111af27a92 Mon Sep 17 00:00:00 2001 From: minani-0621 Date: Tue, 20 Aug 2024 11:11:24 +0900 Subject: [PATCH 01/25] =?UTF-8?q?refactor:=20=EB=A0=88=EC=9D=B4=EC=8B=B1?= =?UTF-8?q?=20=EA=B2=8C=EC=9E=84=20=EC=98=A4=EB=94=94=EC=98=A4=20=EC=A0=9C?= =?UTF-8?q?=EC=96=B4=20=EB=A1=9C=EC=A7=81=EC=9D=84=20useRacingGameAudio=20?= =?UTF-8?q?=EC=BB=A4=EC=8A=A4=ED=85=80=20=ED=9B=85=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(CC-169)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Caecae/src/hooks/useRacingGameAudio.tsx | 82 +++++++++++++++++++++++++ Caecae/src/utils/audioManipulate.tsx | 39 ------------ 2 files changed, 82 insertions(+), 39 deletions(-) create mode 100644 Caecae/src/hooks/useRacingGameAudio.tsx delete mode 100644 Caecae/src/utils/audioManipulate.tsx diff --git a/Caecae/src/hooks/useRacingGameAudio.tsx b/Caecae/src/hooks/useRacingGameAudio.tsx new file mode 100644 index 0000000..d43ed59 --- /dev/null +++ b/Caecae/src/hooks/useRacingGameAudio.tsx @@ -0,0 +1,82 @@ +import { useRef, useEffect } from "react"; + +const useRacingGameAudio = (playingAudioSrc: string, stopAudioSrc: string) => { + const playingAudioRef = useRef(null); + const stoppingAudioRef = useRef(null); + const stoppingAudioRunRef = useRef(false); + + useEffect(() => { + playingAudioRef.current = new Audio(playingAudioSrc); + stoppingAudioRef.current = new Audio(stopAudioSrc); + + return () => { + resetAudio(playingAudioRef.current); + resetAudio(stoppingAudioRef.current); + }; + }, [playingAudioSrc, stopAudioSrc]); + + const playAudio = (audio: HTMLAudioElement | null) => { + if (audio) { + resetAudio(audio); + const playPromise = audio.play(); + if (playPromise !== undefined) { + playPromise.catch((error) => { + console.error("Audio play error:", error); + }); + } + } + }; + + const resetAudio = (audio: HTMLAudioElement | null) => { + if (audio) { + audio.pause(); + audio.currentTime = 0; + audio.volume = 1.0; + audio.load(); + } + }; + + const fadeOutAudio = ( + audio: HTMLAudioElement | null, + duration: number, + callback: () => void + ) => { + if (!audio) return; + + const step = 0.1; + const fadeInterval = duration / (audio.volume / step); + + const fade = setInterval(() => { + if (audio.volume > step) { + audio.volume -= step; + }else { + clearInterval(fade); + audio.volume = 0; + audio.pause(); + callback(); + } + }, fadeInterval); + }; + + return { + startPlayingAudio: () => { + playAudio(playingAudioRef.current); + }, + startStoppingAudio: () => { + if (!stoppingAudioRunRef.current) { + stoppingAudioRunRef.current = true; + playAudio(stoppingAudioRef.current); + } + }, + fadeOutPlayingAudio: (duration: number, callback: () => void) => { + fadeOutAudio(playingAudioRef.current, duration, callback); + }, + resetAllAudio: () => { + resetAudio(playingAudioRef.current); + resetAudio(stoppingAudioRef.current); + }, + stoppingAudioRunRef + }; +}; + +export default useRacingGameAudio; diff --git a/Caecae/src/utils/audioManipulate.tsx b/Caecae/src/utils/audioManipulate.tsx deleted file mode 100644 index f8a945e..0000000 --- a/Caecae/src/utils/audioManipulate.tsx +++ /dev/null @@ -1,39 +0,0 @@ -export const playAudio = (audio: HTMLAudioElement | null) => { - if(audio){ - resetAudio(audio); - const playPromise = audio.play(); - - if(playPromise !== undefined) { - playPromise.catch(error => { - console.error("Audio play error:", error); - }); - } - } -}; - -export const resetAudio = (audio: HTMLAudioElement | null) => { - if(audio){ - audio.pause(); - audio.currentTime = 0; - audio.volume = 1.0; - audio.load(); - } -}; - -export const fadeOutAudio = (audio: HTMLAudioElement | null, duration: number, callback: () => void) => { - if(!audio) return; - - const step = 0.1; - const fadeInterval = duration / (audio.volume / step); - - const fade = setInterval(() => { - if (audio.volume > step) { - audio.volume -= step; - }else { - clearInterval(fade); - audio.volume = 0; - audio.pause(); - callback(); - } - }, fadeInterval); -}; \ No newline at end of file From 553e6b624ef4bc90d5827241145f95cc239c8e48 Mon Sep 17 00:00:00 2001 From: minani-0621 Date: Tue, 20 Aug 2024 11:12:24 +0900 Subject: [PATCH 02/25] =?UTF-8?q?refactor:=20=EB=A0=88=EC=9D=B4=EC=8B=B1?= =?UTF-8?q?=20=EA=B2=8C=EC=9E=84=EC=97=90=20useRacingGameAudio=20=EC=BB=A4?= =?UTF-8?q?=EC=8A=A4=ED=85=80=20=ED=9B=85=20=EC=A0=81=EC=9A=A9=20(CC-169)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/RacingGame/RacingGame.tsx | 261 +++++++++--------- 1 file changed, 129 insertions(+), 132 deletions(-) diff --git a/Caecae/src/components/RacingGame/RacingGame.tsx b/Caecae/src/components/RacingGame/RacingGame.tsx index b11f03d..79ea360 100644 --- a/Caecae/src/components/RacingGame/RacingGame.tsx +++ b/Caecae/src/components/RacingGame/RacingGame.tsx @@ -11,120 +11,8 @@ import { import { store, useExistState } from "../../shared/Hyundux/index.tsx"; import Link from "../../shared/Hyunouter/Link.tsx"; import getRacingGameTopRate from "../../stories/getRacingGameTopRate.tsx"; -import { resetAudio, playAudio, fadeOutAudio } from "../../utils/audioManipulate.tsx"; import { useDebounce } from "../../hooks/index.tsx"; - -/** 게임 상태에 따라 다르게 보여지는 콘텐츠 */ -const gameContent = ( - gameStatus: string, - distance: number, - topRate: string | null, - isButtonDisabled: boolean, - handlePlayGame: () => void, - enterEvent: () => void -) => { - switch (gameStatus) { - case "previous": - case "enterEvent": - return ( -
-
CASPER ELECTRIC
-
전력으로...!
-
- -
-
- ); - case "playing": - return ( -
-
Game Score
-
{distance.toFixed(3)} KM
-
-
stop :
-
- spacebarBtn -
-
-
- ); - case "end": - return ( -
-
-
Game Score
-
-
- {distance.toFixed(3)} KM -
-
- {`상위 ${topRate}%`} -
-
-
-
- - -
-
- ); - default: - return null; - } -}; - -/** 게임 상태에 따라 다르게 보여지는 우측 상단 메뉴 */ -const gameMenu = (gameStatus: string) => { - switch (gameStatus) { - case "previous": - case "playing": - case "enterEvent": - return ( -
- - - -
- ); - case "end": - return ( -
- - - - -
- ); - default: - return null; - } -}; +import useRacingGameAudio from "../../hooks/useRacingGameAudio.tsx"; const RacingGame: React.FC = () => { const lottieRef = useRef(null); @@ -144,11 +32,19 @@ const RacingGame: React.FC = () => { const frontAnimationControls = useAnimation(); const rearAnimationControls = useAnimation(); - const playingSoundRef = useRef(null); - const stopSoundRef = useRef(null); - const stopSoundPlayedRef = useRef(false); const endGameTimeoutRef = useRef(null); + const { + startPlayingAudio, + startStoppingAudio, + fadeOutPlayingAudio, + resetAllAudio, + stoppingAudioRunRef, + } = useRacingGameAudio( + "/assets/audio/racingGamePlayingSound.wav", + "/assets/audio/racingGameStopSound.wav" + ); + /** 이동한 km를 구하는 함수 */ const calculateDistance = (x: number) => { const totalDistance = Math.abs(x); @@ -180,12 +76,7 @@ const RacingGame: React.FC = () => { transition: { duration: 1, ease: "easeOut" }, }); - fadeOutAudio(playingSoundRef.current, 1000, () => { - if (!stopSoundPlayedRef.current) { - stopSoundPlayedRef.current = true; - playAudio(stopSoundRef.current); - } - }); + fadeOutPlayingAudio(1000, startStoppingAudio); } }; @@ -202,8 +93,8 @@ const RacingGame: React.FC = () => { setIsButtonDisabled(true); setTopRate("?"); - stopSoundPlayedRef.current = false; - playAudio(playingSoundRef.current); + stoppingAudioRunRef.current = false; + startPlayingAudio(); store.dispatch(action.gameStart()); @@ -232,8 +123,6 @@ const RacingGame: React.FC = () => { } }; - - const enterEvent = () => { store.dispatch(action.enterEvent()); } @@ -251,13 +140,9 @@ const RacingGame: React.FC = () => { setRearBackgroundWidth(rearBackgroundImg.width); }; - playingSoundRef.current = new Audio("/assets/audio/racingGamePlayingSound.wav"); - stopSoundRef.current = new Audio("/assets/audio/racingGameStopSound.wav"); - return () => { - resetAudio(playingSoundRef.current); - resetAudio(stopSoundRef.current); - if (endGameTimeoutRef.current) { + resetAllAudio(); + if(endGameTimeoutRef.current) { clearTimeout(endGameTimeoutRef.current); } }; @@ -343,4 +228,116 @@ const RacingGame: React.FC = () => { ); }; +/** 게임 상태에 따라 다르게 보여지는 콘텐츠 */ +const gameContent = ( + gameStatus: string, + distance: number, + topRate: string | null, + isButtonDisabled: boolean, + handlePlayGame: () => void, + enterEvent: () => void +) => { + switch (gameStatus) { + case "previous": + case "enterEvent": + return ( +
+
CASPER ELECTRIC
+
전력으로...!
+
+ +
+
+ ); + case "playing": + return ( +
+
Game Score
+
{distance.toFixed(3)} KM
+
+
stop :
+
+ spacebarBtn +
+
+
+ ); + case "end": + return ( +
+
+
Game Score
+
+
+ {distance.toFixed(3)} KM +
+
+ {`상위 ${topRate}%`} +
+
+
+
+ + +
+
+ ); + default: + return null; + } +}; + +/** 게임 상태에 따라 다르게 보여지는 우측 상단 메뉴 */ +const gameMenu = (gameStatus: string) => { + switch (gameStatus) { + case "previous": + case "playing": + case "enterEvent": + return ( +
+ + + +
+ ); + case "end": + return ( +
+ + + + +
+ ); + default: + return null; + } +}; + export default RacingGame; \ No newline at end of file From 58c36e83d2c62d613a4be46fd134acf237cbaffa Mon Sep 17 00:00:00 2001 From: minani-0621 Date: Wed, 21 Aug 2024 12:11:05 +0900 Subject: [PATCH 03/25] =?UTF-8?q?fix:=20SelectCustom=EC=97=90=20id=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Caecae/src/pages/RacingGame/Enter/SelectCustom.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Caecae/src/pages/RacingGame/Enter/SelectCustom.tsx b/Caecae/src/pages/RacingGame/Enter/SelectCustom.tsx index 17ee088..27259a0 100644 --- a/Caecae/src/pages/RacingGame/Enter/SelectCustom.tsx +++ b/Caecae/src/pages/RacingGame/Enter/SelectCustom.tsx @@ -13,11 +13,11 @@ const SelectCustom = () => { const [selectedOption, setSelectedOption] = useState(null); const [enterable, setEnterable] = useState(false); const options: Option[] = [ - { id: 1, imgSrc: "/assets/racingGameCase1Image.svg", title: "Case 1. 공간활용의 기술", description: "캐스퍼 일렉트릭의 구석구석을\n활용해 많은 물건도 알차게 실을래요." }, - { id: 2, imgSrc: "/assets/racingGameCase2Image.svg", title: "Case 2. 레저의 정석", description: "캐스퍼 일렉트릭과 함께 방방곡곡\n누빌 레저 라이프가 기대되어요." }, - { id: 3, imgSrc: "/assets/racingGameCase3Image.svg", title: <>Case 3. 여행의 정석 Camping, description: "캐스퍼 일렉트릭과 함께 아웃도어\n활동을 쉽고 편안하게 할래요." }, - { id: 4, imgSrc: "/assets/racingGameCase4Image.svg", title: <>Case 4. 여행의 정석 Picnic, description: "캐스퍼 일렉트릭과 함께하는 즐거운\n피크닉이 기대되어요." }, - { id: 5, imgSrc: "/assets/racingGameCase5Image.svg", title: "Case 5. 펫 프렌들리", description: "캐스퍼 일렉트릭으로 반려동물과\n편안하고 안전한 여행을 할래요." }, + { id: 0, imgSrc: "/assets/racingGameCase1Image.svg", title: "Case 1. 공간활용의 기술", description: "캐스퍼 일렉트릭의 구석구석을\n활용해 많은 물건도 알차게 실을래요." }, + { id: 1, imgSrc: "/assets/racingGameCase2Image.svg", title: "Case 2. 레저의 정석", description: "캐스퍼 일렉트릭과 함께 방방곡곡\n누빌 레저 라이프가 기대되어요." }, + { id: 2, imgSrc: "/assets/racingGameCase3Image.svg", title: <>Case 3. 여행의 정석 Camping, description: "캐스퍼 일렉트릭과 함께 아웃도어\n활동을 쉽고 편안하게 할래요." }, + { id: 3, imgSrc: "/assets/racingGameCase4Image.svg", title: <>Case 4. 여행의 정석 Picnic, description: "캐스퍼 일렉트릭과 함께하는 즐거운\n피크닉이 기대되어요." }, + { id: 4, imgSrc: "/assets/racingGameCase5Image.svg", title: "Case 5. 펫 프렌들리", description: "캐스퍼 일렉트릭으로 반려동물과\n편안하고 안전한 여행을 할래요." }, ]; const handleOptionSelect = (id: number) => { From 186f3f4840cfbe186950ba7597935ca4d5c18efa Mon Sep 17 00:00:00 2001 From: minani-0621 Date: Wed, 21 Aug 2024 12:12:20 +0900 Subject: [PATCH 04/25] =?UTF-8?q?fix:=20=EB=A0=88=EC=9D=B4=EC=8B=B1=20?= =?UTF-8?q?=EA=B2=8C=EC=9E=84=20=EC=A0=90=EC=88=98=20=EB=B0=B1=EB=B6=84?= =?UTF-8?q?=EC=9C=84=20api=20=EA=B5=AC=ED=98=84=EC=97=90=20story=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Caecae/src/stories/getRacingGameTopRate.tsx | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/Caecae/src/stories/getRacingGameTopRate.tsx b/Caecae/src/stories/getRacingGameTopRate.tsx index 5bad83a..6bc482c 100644 --- a/Caecae/src/stories/getRacingGameTopRate.tsx +++ b/Caecae/src/stories/getRacingGameTopRate.tsx @@ -1,16 +1,17 @@ +import { Story } from "../shared/Hyundux-saga"; import huynxios from "../shared/Hyunxios"; +import Response from "../utils/Response"; -interface Response { - responseCode: number; - message: string; - data: Data; -} - -interface Data { +export interface RacingGameTopRateDTO { percent: number; } -export default async function getRacingGameTopRate(score: number) { - const response = await huynxios.get("/api/racing/percent?distance=" + score); +const getRacingGameTopRateStory: Story = async (request: object) => { + const distanceObject = request as { distance: string} + const url = `/api/racing/percent?distance=${distanceObject.distance}` + const response = await huynxios.get>(url); + return response; -} \ No newline at end of file +}; + +export { getRacingGameTopRateStory }; \ No newline at end of file From ef87a43da837c39beaea9b44e51aff7b13c768b8 Mon Sep 17 00:00:00 2001 From: minani-0621 Date: Wed, 21 Aug 2024 12:22:55 +0900 Subject: [PATCH 05/25] =?UTF-8?q?fix:=20topRate=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=EB=A5=BC=20RacingGameWork=EC=97=90=20=EC=A0=81=EC=9A=A9=20?= =?UTF-8?q?=ED=9B=84=20useSaga=EB=A1=9C=20=EB=A0=88=EC=9D=B4=EC=8B=B1=20?= =?UTF-8?q?=EA=B2=8C=EC=9E=84=20=EC=A0=90=EC=88=98=20=EB=B0=B1=EB=B6=84?= =?UTF-8?q?=EC=9C=84=20api=20=EC=BD=9C=EC=9D=84=20=EB=A7=88=EC=A7=80?= =?UTF-8?q?=EB=A7=89=20=ED=95=9C=20=EB=B2=88=EB=A7=8C=20=EC=A0=81=EC=9A=A9?= =?UTF-8?q?=EB=90=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/RacingGame/RacingGame.tsx | 89 +++++++------------ Caecae/src/jobs/RacingGame/RacingGameWork.tsx | 19 +++- 2 files changed, 45 insertions(+), 63 deletions(-) diff --git a/Caecae/src/components/RacingGame/RacingGame.tsx b/Caecae/src/components/RacingGame/RacingGame.tsx index 79ea360..2aadf0d 100644 --- a/Caecae/src/components/RacingGame/RacingGame.tsx +++ b/Caecae/src/components/RacingGame/RacingGame.tsx @@ -10,9 +10,9 @@ import { } from "../../jobs/RacingGame/RacingGameWork.tsx"; import { store, useExistState } from "../../shared/Hyundux/index.tsx"; import Link from "../../shared/Hyunouter/Link.tsx"; -import getRacingGameTopRate from "../../stories/getRacingGameTopRate.tsx"; -import { useDebounce } from "../../hooks/index.tsx"; -import useRacingGameAudio from "../../hooks/useRacingGameAudio.tsx"; +import { getRacingGameTopRateStory } from "../../stories/getRacingGameTopRate.tsx"; +import useRacingGameAudio from "../../hooks/useRacingGameAudio.tsx" +import useSaga from "../../shared/Hyundux-saga/useSaga.tsx"; const RacingGame: React.FC = () => { const lottieRef = useRef(null); @@ -21,9 +21,9 @@ const RacingGame: React.FC = () => { const [frontBackgroundWidth, setFrontImageWidth] = useState(0); const [rearBackgroundWidth, setRearBackgroundWidth] = useState(0); const state = useExistState(initRacingGameState); - const [topRate, setTopRate] = useState(null); - const [isButtonDisabled, setIsButtonDisabled] = useState(false); - const debouncedDistance = useDebounce(state.distance, 50); + const [status, teller] = useSaga(); + const animationCompletedRef = useRef(false); + const firstCompleteBlockRef = useRef(false); /** 모션 값을 사용하여 frontBackground의 x 위치 추적 */ const frontX = useMotionValue(0); @@ -78,26 +78,29 @@ const RacingGame: React.FC = () => { fadeOutPlayingAudio(1000, startStoppingAudio); } + + animationCompletedRef.current = true; }; const handleSpacebar = (event: KeyboardEvent) => { - if (event.code === "Space") { + if (event.code === "Space" && state.gameStatus === "playing" && !animationCompletedRef.current) { + // animationCompletedRef.current = true event.preventDefault(); + document.removeEventListener("keydown", handleSpacebar); handleSmoothlyStop(); - store.dispatch(action.gameEnd()); } }; const handlePlayGame = () => { - setIsButtonDisabled(true); - setTopRate("?"); - - stoppingAudioRunRef.current = false; + stoppingAudioRunRef.current = false startPlayingAudio(); store.dispatch(action.gameStart()); + animationCompletedRef.current = false; + firstCompleteBlockRef.current = false; + if (lottieRef.current) { lottieRef.current?.play(); @@ -105,10 +108,6 @@ const RacingGame: React.FC = () => { .start({ x: [0, -14000], transition: { duration: 7, repeat: 0 }, - }) - .then(() => { - handleSmoothlyStop(); - store.dispatch(action.gameEnd()); }); rearAnimationControls.start({ @@ -118,7 +117,6 @@ const RacingGame: React.FC = () => { endGameTimeoutRef.current = setTimeout(() => { handleSmoothlyStop(); - store.dispatch(action.gameEnd()); }, 6000); } }; @@ -149,11 +147,12 @@ const RacingGame: React.FC = () => { }, []); useEffect(() => { - if (state.gameStatus === "playing") { + if (state.gameStatus === "playing" && !animationCompletedRef.current) { document.addEventListener("keydown", handleSpacebar); } else { document.removeEventListener("keydown", handleSpacebar); - } + } + return () => { document.removeEventListener("keydown", handleSpacebar); }; @@ -168,32 +167,6 @@ const RacingGame: React.FC = () => { return () => unsubscribeFrontX(); }, [frontX]); - useEffect(() => { - const fetchData = async () => { - try { - const response = await getRacingGameTopRate(debouncedDistance); - setTopRate(response.data.percent.toFixed(3)); - } catch (error) { - console.error("레이싱 게임 점수 백분위 API 호출 오류:", error); - setTopRate(null); - } - }; - - if(debouncedDistance > 0 && state.gameStatus === "end"){ - fetchData(); - } - }, [debouncedDistance]); - - useEffect(() => { - if (state.gameStatus === "end") { - const timer = setTimeout(() => { - setIsButtonDisabled(false); - }, 2000); - - return () => clearTimeout(timer); - } - }, [state.gameStatus]); - return (
{ x: frontX, }} animate={frontAnimationControls} + onAnimationComplete={() => { + if (firstCompleteBlockRef.current) { + teller(action.gameEnd, getRacingGameTopRateStory, { distance: state.distance }); + }else { + firstCompleteBlockRef.current = true + } + }} /> { autoplay={false} className="absolute top-[485px] left-[250px] w-[350px] h-auto z-[3]" /> - {gameContent(state.gameStatus, state.distance, topRate, isButtonDisabled, handlePlayGame, enterEvent)} + {gameContent(state.gameStatus, state.distance, state.topRate, handlePlayGame, enterEvent)} {gameMenu(state.gameStatus)}
); @@ -232,8 +212,7 @@ const RacingGame: React.FC = () => { const gameContent = ( gameStatus: string, distance: number, - topRate: string | null, - isButtonDisabled: boolean, + topRate: number, handlePlayGame: () => void, enterEvent: () => void ) => { @@ -278,27 +257,19 @@ const gameContent = ( {distance.toFixed(3)} KM
- {`상위 ${topRate}%`} + {`상위 ${topRate.toFixed(3)}%`}
- - + From 7efcbb06701cfbe02d01153ceb8dc855b8e7a21a Mon Sep 17 00:00:00 2001 From: minani-0621 Date: Wed, 21 Aug 2024 20:59:51 +0900 Subject: [PATCH 19/25] =?UTF-8?q?feat:=20=EB=A0=88=EC=9D=B4=EC=8B=B1=20?= =?UTF-8?q?=EA=B2=8C=EC=9E=84=20=EA=B8=B0=EB=A1=9D=20=EC=9E=90=EB=9E=91?= =?UTF-8?q?=ED=95=98=EA=B8=B0=20=EB=B2=84=ED=8A=BC=20=ED=81=B4=EB=A6=AD=20?= =?UTF-8?q?=EC=8B=9C=20=ED=86=A0=EC=8A=A4=ED=8A=B8=20=EC=95=A0=EB=8B=88?= =?UTF-8?q?=EB=A9=94=EC=9D=B4=EC=85=98=20=EA=B5=AC=ED=98=84=20(CC-174)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/RacingGame/RacingGame.tsx | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/Caecae/src/components/RacingGame/RacingGame.tsx b/Caecae/src/components/RacingGame/RacingGame.tsx index 317dacb..885f048 100644 --- a/Caecae/src/components/RacingGame/RacingGame.tsx +++ b/Caecae/src/components/RacingGame/RacingGame.tsx @@ -26,6 +26,9 @@ const RacingGame: React.FC = () => { status; const animationCompletedRef = useRef(false); const firstCompleteBlockRef = useRef(false); + const [showMessage, setShowMessage] = useState(false); + const [animate, setAnimate] = useState(false); + const [isAnimating, setIsAnimating] = useState(false); /** 모션 값을 사용하여 frontBackground의 x 위치 추적 */ const frontX = useMotionValue(0); @@ -128,6 +131,11 @@ const RacingGame: React.FC = () => { } const shareGameScore = () => { + if (isAnimating) return; + setIsAnimating(true); + + console.log(shareGameScore); + const fetchData = async () => { try { const requestData: getRacingGameShortUrlBodyParameter = { @@ -140,7 +148,25 @@ const RacingGame: React.FC = () => { const baseShareUrl = "http://www.caecae.kro.kr/a/" const shareUrl = baseShareUrl + shortUrl; - navigator.clipboard.writeText(shareUrl); + navigator.clipboard.writeText(shareUrl) + .then(() => { + setShowMessage(true); + + setTimeout(() => { + setAnimate(true); + + setTimeout(() => { + setAnimate(false); + + setTimeout(() => { + setShowMessage(false); + setIsAnimating(false); + }, 500); + + }, 3000); + + }, 10); + }); }catch(error) { console.error("단축 Url api 호출 실패:", error); } @@ -228,6 +254,15 @@ const RacingGame: React.FC = () => { /> {gameContent(state.gameStatus, state.distance, state.topRate, handlePlayGame, enterEvent)} {gameMenu(state.gameStatus, shareGameScore)} + {showMessage && ( +
+
+ URL이 복사되었습니다! +
+ 당신의 기록을 공유해보세요! +
+
+ )}
); }; From 86e3abdfb30a468c37c55220b1bbe69b2b81b909 Mon Sep 17 00:00:00 2001 From: minani-0621 Date: Wed, 21 Aug 2024 21:41:31 +0900 Subject: [PATCH 20/25] =?UTF-8?q?fix:=20=EB=B9=8C=EB=93=9C=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EC=88=98=EC=A0=95=20(CC-174)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Caecae/src/pages/RacingGame/Enter/SelectCustom.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/Caecae/src/pages/RacingGame/Enter/SelectCustom.tsx b/Caecae/src/pages/RacingGame/Enter/SelectCustom.tsx index 8a5489a..3e98a63 100644 --- a/Caecae/src/pages/RacingGame/Enter/SelectCustom.tsx +++ b/Caecae/src/pages/RacingGame/Enter/SelectCustom.tsx @@ -1,6 +1,5 @@ import { useState } from "react"; import { action } from "../../../jobs/Overlay/OverlayWork"; -import { useSaga } from "../../../shared/Hyundux-saga"; import { setRacingGameOption } from "../../../stories/getRacingGameOption"; import { store } from "../../../shared/Hyundux"; From 12f91a51915ae65f0e9a2c6849eeff5beec6a704 Mon Sep 17 00:00:00 2001 From: minani-0621 Date: Thu, 22 Aug 2024 01:06:34 +0900 Subject: [PATCH 21/25] =?UTF-8?q?fix:=20=EB=A0=88=EC=9D=B4=EC=8B=B1=20?= =?UTF-8?q?=EA=B2=8C=EC=9E=84=20=EC=9D=91=EB=AA=A8=20=EA=B3=BC=EC=A0=95?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=BB=A4=EC=8A=A4=ED=85=80=20=EC=98=B5?= =?UTF-8?q?=EC=85=98=EC=9D=84=20=EC=84=A0=ED=83=9D=ED=95=A0=20=EB=95=8C=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=9E=9C=EB=8D=94=EB=A7=81=20?= =?UTF-8?q?=EC=A0=84=EC=97=90=20=EB=8B=A4=EB=A5=B8=20=EC=98=81=EC=97=AD?= =?UTF-8?q?=EA=B3=BC=20=EA=B2=B9=EC=B9=98=EB=8A=94=20=EB=B2=84=EA=B7=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=98=A4=ED=83=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Caecae/src/pages/RacingGame/Enter/SelectCustom.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Caecae/src/pages/RacingGame/Enter/SelectCustom.tsx b/Caecae/src/pages/RacingGame/Enter/SelectCustom.tsx index 3e98a63..2bf96c2 100644 --- a/Caecae/src/pages/RacingGame/Enter/SelectCustom.tsx +++ b/Caecae/src/pages/RacingGame/Enter/SelectCustom.tsx @@ -39,7 +39,7 @@ const SelectCustom = ({ phoneNumber }: SelectCustomProps) => { className="flex flex-col cursor-pointer" onClick={() => handleOptionSelect(option.id)} > -
+
{`option${option.id}`}
@@ -145,7 +145,7 @@ const options: Option[] = [ imgSrc: "/assets/racingGameCase3Image.svg", title: ( - Case 3. 여행의 정석 Camping + Case 3. 여행의 정석 Camping ), description: From 2322d790ffb9e3da78e956cadf2eb29913ec74fe Mon Sep 17 00:00:00 2001 From: minani-0621 Date: Thu, 22 Aug 2024 01:14:32 +0900 Subject: [PATCH 22/25] =?UTF-8?q?fix:=20=EB=A0=88=EC=9D=B4=EC=8B=B1=20?= =?UTF-8?q?=EA=B2=8C=EC=9E=84=20=EC=9D=91=EB=AA=A8=20=EA=B3=BC=EC=A0=95?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=9D=B4=EB=AF=B8=20=EC=BB=A4=EC=8A=A4?= =?UTF-8?q?=ED=85=80=20=EC=98=B5=EC=85=98=EC=9D=84=20=EC=84=A0=ED=83=9D?= =?UTF-8?q?=ED=95=9C=20=EC=82=AC=EC=9A=A9=EC=9E=90=EB=8F=84=20=EC=9D=91?= =?UTF-8?q?=EB=AA=A8=EC=99=84=EB=A3=8C=EC=B0=BD=EC=9D=84=20=EB=B3=BC=20?= =?UTF-8?q?=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Caecae/src/pages/RacingGame/Enter/SelectCustom.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Caecae/src/pages/RacingGame/Enter/SelectCustom.tsx b/Caecae/src/pages/RacingGame/Enter/SelectCustom.tsx index 2bf96c2..e1065bd 100644 --- a/Caecae/src/pages/RacingGame/Enter/SelectCustom.tsx +++ b/Caecae/src/pages/RacingGame/Enter/SelectCustom.tsx @@ -106,7 +106,7 @@ const SelectCustom = ({ phoneNumber }: SelectCustomProps) => {
{ setRacingGameOption(phoneNumber, selectedOptionId); - store.dispatch(action.toggleOverlay()); + store.dispatch(action.nextPage()); }} className="bg-[#002C5F] h-[12%] flex items-center justify-center hover:cursor-pointer" > From 5f24d9433eb13178c539583e69f26a2431fe6d6f Mon Sep 17 00:00:00 2001 From: pccommen Date: Thu, 22 Aug 2024 10:26:26 +0900 Subject: [PATCH 23/25] =?UTF-8?q?remove:=20useRacingGameAudio=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20(CC-174)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Caecae/src/hooks/useRacingGameAudio.tsx | 82 ------------------------- 1 file changed, 82 deletions(-) delete mode 100644 Caecae/src/hooks/useRacingGameAudio.tsx diff --git a/Caecae/src/hooks/useRacingGameAudio.tsx b/Caecae/src/hooks/useRacingGameAudio.tsx deleted file mode 100644 index d43ed59..0000000 --- a/Caecae/src/hooks/useRacingGameAudio.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { useRef, useEffect } from "react"; - -const useRacingGameAudio = (playingAudioSrc: string, stopAudioSrc: string) => { - const playingAudioRef = useRef(null); - const stoppingAudioRef = useRef(null); - const stoppingAudioRunRef = useRef(false); - - useEffect(() => { - playingAudioRef.current = new Audio(playingAudioSrc); - stoppingAudioRef.current = new Audio(stopAudioSrc); - - return () => { - resetAudio(playingAudioRef.current); - resetAudio(stoppingAudioRef.current); - }; - }, [playingAudioSrc, stopAudioSrc]); - - const playAudio = (audio: HTMLAudioElement | null) => { - if (audio) { - resetAudio(audio); - const playPromise = audio.play(); - if (playPromise !== undefined) { - playPromise.catch((error) => { - console.error("Audio play error:", error); - }); - } - } - }; - - const resetAudio = (audio: HTMLAudioElement | null) => { - if (audio) { - audio.pause(); - audio.currentTime = 0; - audio.volume = 1.0; - audio.load(); - } - }; - - const fadeOutAudio = ( - audio: HTMLAudioElement | null, - duration: number, - callback: () => void - ) => { - if (!audio) return; - - const step = 0.1; - const fadeInterval = duration / (audio.volume / step); - - const fade = setInterval(() => { - if (audio.volume > step) { - audio.volume -= step; - }else { - clearInterval(fade); - audio.volume = 0; - audio.pause(); - callback(); - } - }, fadeInterval); - }; - - return { - startPlayingAudio: () => { - playAudio(playingAudioRef.current); - }, - startStoppingAudio: () => { - if (!stoppingAudioRunRef.current) { - stoppingAudioRunRef.current = true; - playAudio(stoppingAudioRef.current); - } - }, - fadeOutPlayingAudio: (duration: number, callback: () => void) => { - fadeOutAudio(playingAudioRef.current, duration, callback); - }, - resetAllAudio: () => { - resetAudio(playingAudioRef.current); - resetAudio(stoppingAudioRef.current); - }, - stoppingAudioRunRef - }; -}; - -export default useRacingGameAudio; From cbc9fe95ac429999991141e4d50535eb13304860 Mon Sep 17 00:00:00 2001 From: minani-0621 Date: Thu, 22 Aug 2024 14:49:43 +0900 Subject: [PATCH 24/25] =?UTF-8?q?fix:=20useDebounce=20=EC=BB=A4=EC=8A=A4?= =?UTF-8?q?=ED=85=80=20=ED=9B=85=20=EC=82=AD=EC=A0=9C=20(CC-174)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Caecae/src/hooks/index.tsx | 4 ++-- Caecae/src/hooks/useDebounce.tsx | 19 ------------------- 2 files changed, 2 insertions(+), 21 deletions(-) delete mode 100644 Caecae/src/hooks/useDebounce.tsx diff --git a/Caecae/src/hooks/index.tsx b/Caecae/src/hooks/index.tsx index d6e389c..b560646 100644 --- a/Caecae/src/hooks/index.tsx +++ b/Caecae/src/hooks/index.tsx @@ -2,12 +2,12 @@ import useChangeDependency from "./useChangeDependency"; import useComponentPosition from "./useComponentRect"; import useForceRendering from "./useForceRendering"; import useSpecificTimeEffect from "./useSpecificTimeEffect"; -import useDebounce from "./useDebounce"; +import useAudio from "./useAudio"; export { useChangeDependency, useComponentPosition, useForceRendering, useSpecificTimeEffect, - useDebounce, + useAudio, }; diff --git a/Caecae/src/hooks/useDebounce.tsx b/Caecae/src/hooks/useDebounce.tsx deleted file mode 100644 index aa24c37..0000000 --- a/Caecae/src/hooks/useDebounce.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { useState, useEffect } from 'react'; - -function useDebounce(value: T, delay: number): T { - const [debouncedValue, setDebouncedValue] = useState(value); - - useEffect(() => { - const timer = setTimeout(() => { - setDebouncedValue(value); - }, delay); - - return () => { - clearTimeout(timer); - }; - }, [value]); - - return debouncedValue; -} - -export default useDebounce; From 7e6188facf9d7c17799ff99cccafe767c142457b Mon Sep 17 00:00:00 2001 From: minani-0621 Date: Thu, 22 Aug 2024 14:51:38 +0900 Subject: [PATCH 25/25] =?UTF-8?q?fix:=20=EB=A0=88=EC=9D=B4=EC=8B=B1=20?= =?UTF-8?q?=EA=B2=8C=EC=9E=84=EC=97=90=20useAudio=20=EC=BB=A4=EC=8A=A4?= =?UTF-8?q?=ED=85=80=20=ED=9B=85=20=EC=A0=81=EC=9A=A9=20(CC-174)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/RacingGame/RacingGame.tsx | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/Caecae/src/components/RacingGame/RacingGame.tsx b/Caecae/src/components/RacingGame/RacingGame.tsx index 885f048..6669840 100644 --- a/Caecae/src/components/RacingGame/RacingGame.tsx +++ b/Caecae/src/components/RacingGame/RacingGame.tsx @@ -11,7 +11,7 @@ import { import { store, useExistState } from "../../shared/Hyundux/index.tsx"; import Link from "../../shared/Hyunouter/Link.tsx"; import { getRacingGameTopRateStory } from "../../stories/RacingGame/getRacingGameTopRate.tsx"; -import useRacingGameAudio from "../../hooks/useRacingGameAudio.tsx" +import { useAudio } from "../../hooks/index.tsx"; import useSaga from "../../shared/Hyundux-saga/useSaga.tsx"; import getRacingGameShortUrl, { getRacingGameShortUrlBodyParameter } from "../../stories/RacingGame/getRacingGameShortUrl.tsx"; @@ -22,8 +22,7 @@ const RacingGame: React.FC = () => { const [frontBackgroundWidth, setFrontImageWidth] = useState(0); const [rearBackgroundWidth, setRearBackgroundWidth] = useState(0); const state = useExistState(initRacingGameState); - const [status, teller] = useSaga(); - status; + const [, teller] = useSaga(); const animationCompletedRef = useRef(false); const firstCompleteBlockRef = useRef(false); const [showMessage, setShowMessage] = useState(false); @@ -40,15 +39,12 @@ const RacingGame: React.FC = () => { const endGameTimeoutRef = useRef(null); const { - startPlayingAudio, - startStoppingAudio, - fadeOutPlayingAudio, - resetAllAudio, - stoppingAudioRunRef, - } = useRacingGameAudio( - "/assets/audio/racingGamePlayingSound.wav", - "/assets/audio/racingGameStopSound.wav" - ); + audio: playingMusic, + playAudio: playingMusicPlay, + resetAudio: playingMusicReset, + } = useAudio("/assets/audio/racingGamePlayingSound.wav"); + const { playAudio: stopingMusicPlay, resetAudio: stopingMusicReset } = + useAudio("/assets/audio/racingGameStopSound.wav"); /** 이동한 km를 구하는 함수 */ const calculateDistance = (x: number) => { @@ -81,12 +77,28 @@ const RacingGame: React.FC = () => { transition: { duration: 1, ease: "easeOut" }, }); - fadeOutPlayingAudio(1000, startStoppingAudio); + fadeOutStopingMusic(); } animationCompletedRef.current = true; }; + const fadeOutStopingMusic = () => { + const step = 0.1; + const duration = 1000; + const fadeInterval = duration / (playingMusic.volume / step); + + const fade = setInterval(() => { + if (playingMusic.volume > step) { + playingMusic.volume -= step; + } else { + clearInterval(fade); + playingMusicReset(); + stopingMusicPlay(); + } + }, fadeInterval); + }; + const handleSpacebar = (event: KeyboardEvent) => { if (event.code === "Space" && state.gameStatus === "playing" && !animationCompletedRef.current) { // animationCompletedRef.current = true @@ -98,8 +110,8 @@ const RacingGame: React.FC = () => { }; const handlePlayGame = () => { - stoppingAudioRunRef.current = false - startPlayingAudio(); + stopingMusicReset(); + playingMusicPlay(); store.dispatch(action.gameStart()); @@ -189,7 +201,8 @@ const RacingGame: React.FC = () => { }; return () => { - resetAllAudio(); + playingMusicReset() + stopingMusicReset() if(endGameTimeoutRef.current) { clearTimeout(endGameTimeoutRef.current); }