diff --git a/src/components/display/EmojiAnimation.tsx b/src/components/display/EmojiAnimation.tsx new file mode 100644 index 0000000..057e59f --- /dev/null +++ b/src/components/display/EmojiAnimation.tsx @@ -0,0 +1,20 @@ +import React, { useState, useEffect } from "react"; +import { ZoomAnimation } from "@/pages/ResultLoading.style"; + + +const EmojiAnimation: React.FC = () => { + const emojis = ["🐢", "πŸƒβ€β™‚οΈ", "πŸ‹οΈ", "πŸ“", "🎀", "⚽", "🎸"]; + const [currentEmojiIndex, setCurrentEmojiIndex] = useState(0); + + useEffect(() => { + const emojiInterval = setInterval(() => { + setCurrentEmojiIndex((prevIndex) => (prevIndex + 1) % emojis.length); + }, 1000); // 1μ΄ˆλ§ˆλ‹€ 이λͺ¨μ§€λ₯Ό λ³€κ²½ + + return () => clearInterval(emojiInterval); + }, []); + + return {emojis[currentEmojiIndex]}; +}; + +export default EmojiAnimation; diff --git a/src/components/display/TypingAnimation.tsx b/src/components/display/TypingAnimation.tsx new file mode 100644 index 0000000..0cd345b --- /dev/null +++ b/src/components/display/TypingAnimation.tsx @@ -0,0 +1,77 @@ +import React, { useState, useEffect } from "react"; +import Hangul from "hangul-js"; +import { TypingText } from "@/pages/ResultLoading.style"; + +interface TypingAnimationProps { + texts: string[]; + typingInterval: number; + deleteInterval: number; + waitInterval: number; +} + +const TypingAnimation: React.FC = ({ + texts, + typingInterval, + deleteInterval, + waitInterval, +}) => { + const [displayedText, setDisplayedText] = useState(""); + const [currentTextIndex, setCurrentTextIndex] = useState(0); + const [isCursorBlinking, setIsCursorBlinking] = useState(false); + + useEffect(() => { + const disassembled = Hangul.disassemble(texts[currentTextIndex]); + let index = 0; + let isDeleting = false; + let typeTimeout: NodeJS.Timeout; + + const handleTyping = () => { + if (!isDeleting) { + setIsCursorBlinking(false); // 타이핑 μ€‘μ—λŠ” μ»€μ„œ κΉœλΉ‘μž„ λΉ„ν™œμ„±ν™” + + if (index <= disassembled.length) { + const nextText = Hangul.assemble(disassembled.slice(0, index)); + setDisplayedText(nextText); + index++; + } else { + setIsCursorBlinking(true); // λͺ¨λ“  ν…μŠ€νŠΈκ°€ νƒ€μ΄ν•‘λ˜λ©΄ μ»€μ„œ κΉœλΉ‘μž„ ν™œμ„±ν™” + setTimeout(() => { + isDeleting = true; + index = texts[currentTextIndex].length; + setIsCursorBlinking(false); // μ‚­μ œ 쀑에도 μ»€μ„œ κΉœλΉ‘μž„ λΉ„ν™œμ„±ν™” + handleTyping(); + }, waitInterval); + return; + } + } else { + if (index > 0) { + setDisplayedText((prevText) => prevText.slice(0, index - 1)); + index--; + } else { + setCurrentTextIndex((prevIndex) => (prevIndex + 1) % texts.length); + isDeleting = false; + setDisplayedText(""); + clearTimeout(typeTimeout); + setIsCursorBlinking(true); // λ‹€μŒ ν…μŠ€νŠΈλ‘œ μ΄λ™ν•˜κΈ° μ „ κΉœλΉ‘μž„ ν™œμ„±ν™” + return; + } + } + + typeTimeout = setTimeout(handleTyping, isDeleting ? deleteInterval : typingInterval); + }; + + typeTimeout = setTimeout(handleTyping, typingInterval); + + return () => clearTimeout(typeTimeout); + }, [currentTextIndex]); + + return ( + +

+ {displayedText} +

+
+ ); +}; + +export default TypingAnimation; diff --git a/src/pages/ResultLoading.style.ts b/src/pages/ResultLoading.style.ts index 3555f82..ca9056d 100644 --- a/src/pages/ResultLoading.style.ts +++ b/src/pages/ResultLoading.style.ts @@ -27,7 +27,7 @@ export const TypingText = styled.div<{ isCursorBlinking: boolean }>` display: inline-flex; align-items: center; position: relative; - min-height: 1.5em; /* 타이핑 ν…μŠ€νŠΈ μ˜μ—­μ˜ μ΅œμ†Œ 높이 μ„€μ • */ + min-height: 1.5em; &::after { content: ""; @@ -36,9 +36,9 @@ export const TypingText = styled.div<{ isCursorBlinking: boolean }>` height: 1em; background-color: black; position: absolute; - right: -8px; /* 였λ₯Έμͺ½μœΌλ‘œ μ•½κ°„ 이동 */ - bottom: 5px; /* μ•„λž˜μͺ½μœΌλ‘œ μ•½κ°„ 이동 */ - animation: ${({ isCursorBlinking }) => (isCursorBlinking ? 'blink 0.7s steps(1) infinite' : 'none')}; /* 쑰건에 따라 κΉœλΉ‘μž„ */ + right: -8px; + bottom: 5px; + animation: ${({ isCursorBlinking }) => (isCursorBlinking ? 'blink 0.7s steps(1) infinite' : 'none')}; } @keyframes blink { diff --git a/src/pages/ResultLoading.tsx b/src/pages/ResultLoading.tsx index 18b6de4..659f913 100644 --- a/src/pages/ResultLoading.tsx +++ b/src/pages/ResultLoading.tsx @@ -1,85 +1,29 @@ -import React, { useState, useEffect } from "react"; -import Hangul from "hangul-js"; //npm install hangul-js ν•„μš” -import { LoadingContainer, ZoomAnimation, TypingText } from "./ResultLoading.style"; +import React from "react"; +import TypingAnimation from "../components/display/TypingAnimation"; +import EmojiAnimation from "../components/display/EmojiAnimation"; +import { LoadingContainer } from "./ResultLoading.style"; const ResultLoading: React.FC = () => { - const emojis = ["🐢", "πŸƒβ€β™‚οΈ", "πŸ‹οΈ", "πŸ“", "🎀", "⚽", "🎸"]; - const [currentEmojiIndex, setCurrentEmojiIndex] = useState(0); - const [displayedText, setDisplayedText] = useState(""); - const [currentTextIndex, setCurrentTextIndex] = useState(0); - const [isCursorBlinking, setIsCursorBlinking] = useState(false); // μ»€μ„œ κΉœλΉ‘μž„ μƒνƒœ const texts = [ "λ‹Ήμ‹ μ˜ μ·¨ν–₯을 λΆ„μ„ν•˜λŠ” 쀑...", "μž μ‹œλ§Œ κΈ°λ‹€λ € μ£Όμ„Έμš”...", "μ·¨ν–₯ 뢄석 쀑...", - "곧 λ‹Ήμ‹ μ˜ 동아리 운λͺ…을 κ³΅κ°œν•©λ‹ˆλ‹€...!", "동아리 운λͺ… 뢄석 쀑... ", - "... μ„€λ ˆμ§€ μ•Šλ‚˜μš”?", ]; - const typingInterval = 30; // 타이핑 속도 - const deleteInterval = 300 / texts[currentTextIndex].length; // μ‚­μ œ 속도 - const waitInterval = 1500; // 타이핑 이후 μ‚­μ œ μ „κΉŒμ§€ λŒ€κΈ° μ‹œκ°„ - - useEffect(() => { - const emojiInterval = setInterval(() => { - setCurrentEmojiIndex((prevIndex) => (prevIndex + 1) % emojis.length); - }, 1000); // 1μ΄ˆλ§ˆλ‹€ 이λͺ¨μ§€λ₯Ό λ³€κ²½ - - return () => clearInterval(emojiInterval); - }, []); - - useEffect(() => { - const disassembled = Hangul.disassemble(texts[currentTextIndex]); - let index = 0; - let isDeleting = false; - let typeTimeout: NodeJS.Timeout; - - const handleTyping = () => { - if (!isDeleting) { - setIsCursorBlinking(false); // 타이핑 μ€‘μ—λŠ” μ»€μ„œ κΉœλΉ‘μž„μ„ λΉ„ν™œμ„±ν™” - - if (index <= disassembled.length) { - const nextText = Hangul.assemble(disassembled.slice(0, index)); - setDisplayedText(nextText); - index++; - } else { - // λͺ¨λ“  ν…μŠ€νŠΈκ°€ νƒ€μ΄ν•‘λ˜λ©΄ μ»€μ„œ κΉœλΉ‘μž„ ν™œμ„±ν™” - setIsCursorBlinking(true); - setTimeout(() => { - isDeleting = true; - index = texts[currentTextIndex].length; - setIsCursorBlinking(false); // μ‚­μ œ 쀑에도 μ»€μ„œ κΉœλΉ‘μž„ λΉ„ν™œμ„±ν™” - handleTyping(); - }, waitInterval); - return; - } - } else { - if (index > 0) { - setDisplayedText((prevText) => prevText.slice(0, index - 1)); - index--; - } else { - setCurrentTextIndex((prevIndex) => (prevIndex + 1) % texts.length); - isDeleting = false; - setDisplayedText(""); - clearTimeout(typeTimeout); - setIsCursorBlinking(true); // λ‹€μŒ ν…μŠ€νŠΈλ‘œ μ΄λ™ν•˜κΈ° μ „ κΉœλΉ‘μž„ ν™œμ„±ν™” - return; - } - } - - typeTimeout = setTimeout(handleTyping, isDeleting ? deleteInterval : typingInterval); - }; - - typeTimeout = setTimeout(handleTyping, typingInterval); - - return () => clearTimeout(typeTimeout); - }, [currentTextIndex]); + const typingInterval = 30; + const deleteInterval = 10; + const waitInterval = 1500; return ( - {displayedText} - {emojis[currentEmojiIndex]} + + ); };