[React] ๋ฆฌ์กํธ ํ์๊ธฐ(TypeWriter) ํจ๊ณผ ๊ตฌํํ๊ธฐ feat.์ ๋๋ ์ดํฐ
๊ธฐ๋ณธ ๋ก์ง
ํ์๊ธฐ๋ก ํ ๊ธ์์ฉ ์
๋ ฅํ๋ ํจ๊ณผ(Typewriter Effect)๋ ์ด๋ฏธ ์ ๋ง์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์์ง๋ง, setInterval
ํ์ด๋จธ API๋ฅผ ์ด์ฉํด์ ์ง์ ๊ตฌํํ ์ ์๋ค. ์๋ ๋ฌธ์ฅ(ํ
์คํธ)์ ํ ๊ธ์์ฉ ์๋ฅธ ํ ๊ฐ์ฅ ์์ ๊ธ์๋ถํฐ ํ๋์ฉ ์ด์ด ๋ถ์ด๋ ๋ฐฉ์์ด๋ค.
<!-- HTML -->
<div>
<span class="content"></span>
<span class="blink" />
</div>
// JS
const $content = document.querySelector('.content');
function typewriter(target, sentence, speed = 200) {
const split = sentence.split('');
let text = '';
let i = 0;
const timer = setInterval(() => {
if (i < split.length) {
text += split[i++]; // ++๋ ํ์ ์ฆ๊ฐ(์ฆ๊ฐํ๊ธฐ์ ๊ฐ ๋ฐํ)
target.textContent = text;
} else {
clearInterval(timer);
}
}, speed);
}
typewriter($content, 'hello world', 400);
๊น๋นก์ด๋ ์ปค์ ํจ๊ณผ๋ |
์ฝํ
์ธ ๋ฅผ ๊ฐ๋ <span>
ํ๊ทธ์, step-end
, step-start
๊ฐ์ ์ ๋๋ฉ์ด์
์ ์ถ๊ฐํ๋ค. :after
์๋ ํด๋์ค๋ฅผ ์ฌ์ฉํ์ง ์๊ณ <span>|</span>
ํํ๋ก ์
๋ ฅํด๋ ๋๋ค.
/* CSS ๊น๋นก์ด๋ ์ปค์ ํจ๊ณผ */
.blink::after {
content: '|';
}
.blink {
animation: blink 1s step-end infinite;
font-weight: bold;
margin-left: 1px;
}
@keyframes blink {
50% {
opacity: 0;
}
}
React Hook์ผ๋ก ๋ง๋ค๊ธฐ
React์ ์ ์ฉํ ๋๋ setInterval
์ ์ด์ฉํด ์์ ๋น์ทํ ๋ก์ง์ผ๋ก ์์ฑํ๋ฉด ๋๋ค.
- Hook์ด ์ฒ์ ํธ์ถ๋๋ฉด
index
๋ฅผ 1์ฉ ๋ํ๋setInterval
๋ด๋ถ ํจ์๊ฐsec
์ด ๊ฐ๊ฒฉ์ผ๋ก ์คํ๋๋ค. - ์
๋ ฅ๋ฐ์ ๋ฌธ์ฅ(ํ
์คํธ)์
index
๋ฅผ ์ด๊ณผํ๋ฉด ์๋๋ฏ๋กcontent.length - 1
๋ฏธ๋ง๊น์ง๋ง ์คํํ๋ค. index
์ํ๊ฐ ๋ณ๊ฒฝ๋๋ฉด 2๋ฒ์งธuseEffect
๊ฐ ์คํ๋ผ์ ํ์ฌindex
์ ํด๋นํ๋ ๊ธ์๋ฅผ ์ด์ด ๋ถ์ธ๋ค.
// hooks/useTypeWriter.js
export default function useTypeWriter({
content,
sec = 200,
hasBlink = false,
}) {
const [displayedContent, setDisplayedContent] = useState('');
const [index, setIndex] = useState(0);
useEffect(() => {
const animKey = setInterval(() => {
setIndex((index) => {
if (index < content.length - 1) {
return index + 1;
}
clearInterval(animKey);
return index;
});
}, sec);
return () => clearInterval(animKey);
}, []);
useEffect(() => {
setDisplayedContent(
(displayedContent) => displayedContent + content[index],
);
}, [index]);
return (
<>
{displayedContent}
{hasBlink && <span className="blink" />}
</>
);
}
์์ฑํ Hook์ ์๋์ฒ๋ผ ์ํ๋ ์กฐ๊ฑด(๊น๋นก์ ์ปค์ ์ฌ๋ถ, ์ ๋ ฅ ์๋)์ ๋ง์ถฐ ์คํํ ์ ์๋ค.
// Component.js
import useTypeWriter from '@/hooks/useTypeWriter';
export default function Component() {
const typingText = useTypeWriter({
content: 'Hello World',
sec: 200,
hasBlink: true,
});
return <p>{typewriterText}</p>;
}
์ ๋๋ ์ดํฐ ํจ์๋ก ๊ตฌํํ๊ธฐ
๐ก ์ ๋๋ ์ดํฐ ํจ์๋ฅผ ํธ์ถํ๋ฉด ์คํ์ ์ฒ๋ฆฌํ๋ ์ ๋๋ ์ดํฐ ๊ฐ์ฒด๊ฐ ๋ฐํ๋๋ค. ์ ๋๋ ์ดํฐ ํจ์๋ ์ดํฐ๋ฌ๋ธ์ด๋ฉฐ, ํจ์ ์์ฒด์ ์ ๊ฐ ๋ฌธ๋ฒ์ ์ฌ์ฉํ ์๋ ์๋ค.
์ ๋๋ ์ดํฐ ํบ์๋ณด๊ธฐ
์ผ๋ฐ ํจ์๋ 0~1๊ฐ ๊ฐ๋ง ๋ฐํํ ์ ์์ง๋ง ์ ๋๋ ์ดํฐ ํจ์๋ ์ฌ๋ฌ๊ฐ์ ๊ฐ์ ํ์์ ๋ฐ๋ผ ํ๋์ฉ ๋ฐํ(yield
)ํ ์ ์๋ค. next()
๋ฅผ ํธ์ถํ๋ฉด ๊ฐ์ฅ ๊ฐ๊น์ด yield <value>
๋ฌธ์ ๋ง๋ ๋๊น์ง ์คํํ๊ณ value
๋ฅผ ๋ฐํํ๋ค. ์๋ ์์์์ ํจ์ ์์ while(true)
๋ฌธ์ด ์์ผ๋ฏ๋ก next()
๋ฉ์๋๋ฅผ ํธ์ถํ ๋๋ง๋ค 'even'
'odd'
๋ฅผ ๋ฐ๋ณตํด์ ๋ฐํํ๊ณ ์๋ค.
function* zebraGenerator() {
while (true) {
yield 'even';
yield 'odd';
}
}
const zebra = zebraGenerator(); // ์ ๋๋ ์ดํฐ ๊ฐ์ฒด ๋ฐํ
zebra.next(); // {value: 'even', done: false}
zebra.next(); // {value: 'odd', done: false}
์ ๋๋ ์ดํฐ ๋ฌธ๋ฒ์ ํ์ฉํด ์
๋ ฅ๋ฐ์ ๋ฌธ์ฅ์ ํ ๊ธ์์ฉ ์๋ฅธ ๋ค ๊ฐ์ฅ ์ ๊ธ์๋ถํฐ ์ด์ด๋ถ์ด๋๋ก ๋ง๋ค ์ ์๋ค. ๋์ด์ ๋ฐํํ value
๊ฐ ์์ผ๋ฉด(sentenceAsCharArray
๋ฐฐ์ด ์ํ๋ฅผ ๋ง์น๋ฉด) undefined
๋ฅผ ๋ฐํํ๊ณ , ๋ฐํํ๋ ๊ฐ์ฒด๋ด done
์์ฑ์ true
๋ก ๋ณํ๋ค.
function* textGenerator(sentence) {
let text = '';
const sentenceAsCharArray = sentence.split('');
for (const letter of sentenceAsCharArray) {
text += letter;
yield text;
}
}
const generator = textGenerator('HI');
generator.next(); // {value: 'H', done: false}
generator.next(); // {value: 'HI', done: false}
generator.next(); // {value: undefined, done: true}
React์ ์ ์ฉํ๊ธฐ โญ๏ธ
์ ๋ ฅ๋ฐ์ ๋ฌธ์ฅ์ ํ ๊ธ์์ฉ ์๋ฅธ ํ ์ด์ด๋ถ์ด๋ ์ ๋๋ ์ดํฐ ํจ์๋ utils ํ์ผ๋ก ์ฎ๊ฒจ์ ์ฌ์ฌ์ฉํ ์ ์๋๋ก ํ๋ค.
// lib/utils.js
export function* textGenerator(sentence) {
let text = '';
const sentenceAsCharArray = sentence.split('');
for (const letter of sentenceAsCharArray) {
text += letter;
yield text;
}
}
๊ธฐ์กด ์์ฑํ๋ Hook์ useEffect
๋ฅผ 1๋ฒ๋ง ์ฌ์ฉํ๋ ๋ฐฉ์์ผ๋ก ๊น๋ํ๊ฒ ์์ฑํ ์ ์๋ค. setInterval
ํจ์์ ๋๋ ์ด ๊ฐ๊ฒฉ์ ๋ฐ๋ผ generator.next()
๋ฉ์๋๊ฐ ์คํ๋ผ์ ํ ๊ธ์์ฉ ์ด์ด๋ถ์ธ ํ
์คํธ๋ฅผ ๋ฐํํ๋ค
// hooks/useTypeWriter.js
import { textGenerator } from '@/lib/utils';
export default function useTypeWriter({
content,
sec = 100,
hasBlink = false,
}) {
const [displayedContent, setDisplayedContent] = useState('');
useEffect(() => {
const generator = textGenerator(content);
const interval = setInterval(() => {
const { value, done } = generator.next();
if (done) {
clearInterval(interval);
} else {
setDisplayedContent(value);
}
}, sec);
return () => clearInterval(interval);
}, []);
return (
<>
{displayedContent}
{hasBlink && <span className="blink" />}
</>
);
}
์ง์ฐ๊ณ ๋ค์ ์ ๋ ฅํ๋ ํจ๊ณผ ๊ตฌํ โญ๏ธ
Step 1. Backspace ํ์ ๊ณ์ฐ
Hello World
๋ฅผ ์
๋ ฅํ๋ค๊ฐ World
๋ฅผ ์ง์ฐ๊ณ Winter
๋ฅผ ๋ค์ ์
๋ ฅํ๋ค๊ณ ๊ฐ์ ํ๋ฉด ์๋์ฒ๋ผ W
์ด์ ๊น์ง ๋ฐฑ์คํ์ด์ค๋ฅผ ๋๋ฌ์ ํ ๊ธ์์ฉ ์ง์ฐ๊ณ inter
๋ฅผ ์ฐจ๋ก๋๋ก ์
๋ ฅํ๋ ๊ณผ์ ์ ๊ฑฐ์น๋ค.
Hello Worl (Backspace)
Hello Wor (Backspace)
Hello Wo (Backspace)
Hello W (Backspace)
Hello Wi (i ์
๋ ฅ)
Hello Win (in ์
๋ ฅ)
Hello Wint (int ์
๋ ฅ)
Hello Winte (inte ์
๋ ฅ)
Hello Winter (inter ์
๋ ฅ)
d
๋ถํฐ o
(orld
)๊น์ง ์ง์ฐ๊ธฐ ์ํด Backspace๋ฅผ 4๋ฒ ๋๋ ๋ค. Backspace๋ฅผ ๋ช ๋ฒ ๋๋ ๋์ง ์๊ธฐ ์ํด์ ์๋ ๋ฌธ์ฅ ๊ธธ์ด(length
)์์, ์๋ ๋ฌธ์ฅ/๋ฐ๊ฟ ๋ฌธ์ฅ์ ๋ง์ง๋ง ๋์ผ ๊ธ์ ์์น(index + 1
)๋ฅผ ๋นผ๋ฉด ๋๋ค.
Hello World
Hello Winter
------------
1234567 --> 7๋ฒ ์์น(์ธ๋ฑ์ค 6 + 1)๊น์ง ๋์ผ
------------
11(Hello World ๊ธธ์ด) - 7 = 4 -> Hello World์์ Backspace 4๋ฒ
------------
์ ๋ด์ฉ์ ์๋์ฒ๋ผ ํจ์๋ก ์์ฑํ ์ ์๋ค. ํ์ฌ ๋ฐ๋ณต๋ฌธ์ ์ํํ๋ ์ธ๋ฑ์ค์ ๋ํ from
, to
๊ธ์๊ฐ ๊ฐ๋ค๋ฉด charsIn...
๋ณ์ ๊ฐ์ 1์ฉ ๋ํ๊ณ , ๊ฐ์ง ์๋ค๋ฉด ๋ฐ๋ณต๋ฌธ์ ๋ฉ์ถ๊ณ ๊ณ์ฐํ Backspace ํ์๋ฅผ ๋ฐํํ๋ค.
// lib/utils.js
export function calculateBackspaces(from, to) {
let charsInCommonFromStart = 0; // from, to์ ๋ง์ง๋ง ๋์ผ ๊ธ์ ์์น(index + 1)
for (let i = 0; i < from.length; i++) {
const fromChar = from[i];
const toChar = to[i];
if (toChar === fromChar) {
charsInCommonFromStart += 1; // Hello W ๊น์ง ๋์ผํ๋ฏ๋ก 7
} else {
break;
}
}
return from.length - charsInCommonFromStart; // 11 - 7 = 4
}
calculateBackspaces('Hello World', 'Hello Winter'); // 4
Step 2. Backspace ํ์ ๋งํผ ๊ธ์ ์ญ์
Backspace ํ์๋ฅผ ์์์ผ๋ ์ด์ ๊ณ์ฐํ ํ์๋งํผ ๊ธ์๋ฅผ ์ง์์ค๋ค. ์ฒซ๋ฒ์งธ ๋ฌธ์ฅ์ Backspace ํ์๊ฐ 0์ด์ด์ ์ญ์ ํ ๊ธ์๊ฐ ์์ผ๋ฏ๋ก Backspace ํ์ ๋งํผ ๊ธ์๋ฅผ ์ญ์ ํ๋ ๋ฐ๋ณต๋ฌธ์ ๊ทธ๋ฅ ๊ฑด๋๋ด๋ค.
// lib/utils.js
export function* textGenerator(sentences) {
// sentences๋ ['Hello World', 'Hello Winter'] ๋ผ๊ณ ๊ฐ์ (์๋ ๋ฌธ์ฅ, ๋ฐ๊ฟ ๋ฌธ์ฅ)
let text = '';
for (const sentence of sentences) {
// (1) Backspace ํ์ ๊ณ์ฐ
const backspaces = calculateBackspaces(text, sentence);
// (์ฒซ๋ฒ์งธ ๋ฌธ์ฅ) 0
// (๋๋ฒ์งธ ๋ฌธ์ฅ) 4
// (2) ๊ณ์ฐํ Backspace ํ์๋งํผ ๋ค์์ ๋ถํฐ ๊ธ์ ์ญ์
// ์ฒซ๋ฒ์งธ ๋ฌธ์ฅ ํน์ backspaces๊ฐ 0์ด๋ฉด ์๋ ๋ฐ๋ณต๋ฌธ์ ๊ฑด๋๋
for (let i = 0; i < backspaces; i++) {
text = text.slice(0, -1);
console.log(text);
// 2๋ฒ์งธ ๋ฌธ์ฅ๋ถํฐ...
// 'Hello Worl' -> 'Hello Wor' -> 'Hello Wo' -> 'Hello W'
}
}
}
Step 3. ๋ฐ๊ฟ ๋ฌธ์ฅ ์ ๋ ฅ
๐ก ์ฒซ๋ฒ์งธ ๋ฌธ์ฅ(sentence)์ผ ๋ text๊ฐ ๋น ๋ฌธ์์ด์์ ‘H’ → ‘He’ → ... ํ๋์ฉ ์ด์ด ๋ถ์ฌ์ง
Backspace ํ์ ๋งํผ ๋ฌธ์ฅ์ ์ง์ ์ผ๋, ๋ค์ ์ง์ด ๋ด์ฉ์ ์ฑ์์ผ ๋๋ค. ๋จผ์ โ๋น ์ง ๊ธ์๋ฅผ ์ฐพ๊ณ , โ์ฐพ์๋ธ ๋น ์ง ๊ธ์๋ฅผ ํ ๊ธ์์ฉ ์๋ฅธ ๋ค โBackspaceํ์๋งํผ ์ญ์ ํ ๋ฌธ์ฅ์ ์ด์ด๋ถ์ด๋ฉด ๋๋ค.
// lib/utils.js
export function* textGenerator(sentences) {
// sentences๋ ['Hello World', 'Hello Winter'] ๋ผ๊ณ ๊ฐ์ (์๋ ๋ฌธ์ฅ, ๋ฐ๊ฟ ๋ฌธ์ฅ)
let text = '';
for (const sentence of sentences) {
// (1) Backspace ํ์ ๊ณ์ฐ, (2) Backspace ํ์ ๋งํผ ๊ธ์ ์ญ์ ํ๋ ์ฝ๋ ์๋ต...
// (3) ๋ฐ๊ฟ ๋ฌธ์ฅ ์
๋ ฅ
const missingChars = sentence.slice(text.length);
// (์ฒซ๋ฒ์งธ ๋ฌธ์ฅ) 'Hello World'.slice(0) -> 'Hello World'
// (๋๋ฒ์งธ ๋ฌธ์ฅ) 'Hello Winter'.slice(7) -> 'inter'
const missingCharsArray = missingChars.split('');
// (์ฒซ๋ฒ์งธ ๋ฌธ์ฅ) ['H','e','l','l','o',' ','W','o','r','l','d']
// (๋๋ฒ์งธ ๋ฌธ์ฅ) ['i', 'n', 't', 'e', 'r']
for (const missingChar of missingCharsArray) {
text += missingChar;
console.log(text);
// (์ฒซ๋ฒ์งธ ๋ฌธ์ฅ) 'H' -> 'He' -> 'Hel' -> 'Hell' -> 'Hello' -> ...
// (๋๋ฒ์งธ ๋ฌธ์ฅ) 'Hello Wi' -> 'Hello Win' -> 'Hello Wint' -> ...
}
}
}
Step 4. ๋ค์ ๋ฌธ์ฅ์ผ๋ก ๋์ด๊ฐ ๋ ๋๋ ์ด ์ถ๊ฐ
'Hello World'
์
๋ ฅ์ ๋ง์น๊ณ ๋ค์ ๋ฌธ์ฅ 'Hello Winter'
๋ก ๋ฐ๋ก ๋์ด๊ฐ๋ฉด ์ฌ์ฉ์ ์
์ฅ์์ ์ฝ์ ์๊ฐ์ด ๋ถ์กฑํ๋ค. ๋ฐ๋ผ์ ๋ ๋ฌธ์ฅ ์ฌ์ด์ delay ์๊ฐ์ด ํ์ํ๋ค. delay ์ซ์๋งํผ ๋์ผํ text๋ฅผ ๊ณ์ ํ์ํ๋ ๋ฐฉ๋ฒ์ผ๋ก delay ์๊ฐ์ ์ค ์ ์๋ค.
// lib/utils.js
export function* textGenerator(sentences) {
// sentences๋ ['Hello World', 'Hello Winter'] ๋ผ๊ณ ๊ฐ์ (์๋ ๋ฌธ์ฅ, ๋ฐ๊ฟ ๋ฌธ์ฅ)
let text = '';
for (const sentence of sentences) {
// (1) Backspace ํ์ ๊ณ์ฐ, (2) Backspace ํ์ ๋งํผ ๊ธ์ ์ญ์ ํ๋ ์ฝ๋ ์๋ต...
// (3) ๋ฐ๊ฟ ๋ฌธ์ฅ ์
๋ ฅ ์ฝ๋ ์๋ต...
// (4) ๋ค์ ๋ฌธ์ฅ์ผ๋ก ๋์ด๊ฐ๋ ๋๋ ์ด ์ถ๊ฐ
const delay = 15;
for (let i = 0; i < delay; i++) {
console.log(text); // ํ์ดํ ๋๋ ์ด ์๋๊ฐ 100ms๋ผ๋ฉด 100 * 15 = 1500(1.5์ด)๊ฐ ๋๋ ์ด
// (์ฒซ๋ฒ์งธ ๋ฌธ์ฅ) 'Hello World' 15๋ฒ ๋ ๋
// (๋๋ฒ์งธ ๋ฌธ์ฅ) 'Hello Winter' 15๋ฒ ๋ ๋
}
}
}
console.log → yield text ๋ณ๊ฒฝ
Step 1~4์ ์
๋ ฅํ๋ console.log(text)
๋ฅผ yield text
๋ก ๋ณ๊ฒฝํ๋ค. ๊ทธ๋ผ โyield
๋ฌธ์ ๋ง๋ ๋๋ง๋ค text
๊ฐ ํ๋ฉด์ ๋ ๋๋๊ณ , โuseTypeWriter.js → useEffect
๋ด๋ถ์ ์์ฑํ setInterval
ํ์ด๋จธ ์๊ฐ์ด ์ง๋๋ฉด โ๋ฉ์ท๋ ์ ๋๋ ์ดํฐ๊ฐ ๋ค์ ์คํ๋ผ์ ์ด์ for
๋ฌธ์ ์ด์ด์ ์ํํ๋ค.
export function calculateBackspaces(from, to) {
// Hello World (from)
// Hello Winter (to)
// ------------
// 1234567 --> 7๋ฒ ์์น(์ธ๋ฑ์ค 6 + 1)๊น์ง ๋์ผ
// ------------
// 11(Hello World ๊ธธ์ด) - 7 = 4 -> Hello World ์์ Backspace 4๋ฒ
// ------------
let charsInCommonFromStart = 0;
for (let i = 0; i < from.length; i++) {
const fromChar = from[i];
const toChar = to[i];
if (toChar === fromChar) {
charsInCommonFromStart += 1;
} else {
break;
}
}
return from.length - charsInCommonFromStart;
}
export function* textGenerator(sentences) {
// sentences ๋ ['Hello World', 'Hello Winter'] ๋ผ๊ณ ๊ฐ์ (์๋ ๋ฌธ์ฅ, ๋ฐ๊ฟ ๋ฌธ์ฅ)
let text = '';
for (const sentence of sentences) {
// (1) Backspace ํ์ ๊ณ์ฐ
const backspaces = calculateBackspaces(text, sentence);
// (์ฒซ๋ฒ์งธ ๋ฌธ์ฅ) 0
// (๋๋ฒ์งธ ๋ฌธ์ฅ) 4
// (2) ๊ณ์ฐํ Backspace ํ์๋งํผ ๋ค์์ ๋ถํฐ ๊ธ์ ์ญ์
// ์ฒซ๋ฒ์งธ ๋ฌธ์ฅ ํน์ backspaces ๊ฐ 0์ด๋ฉด ์๋ ๋ฐ๋ณต๋ฌธ์ ๊ฑด๋๋
for (let i = 0; i < backspaces; i++) {
text = text.slice(0, -1);
yield text;
// 2๋ฒ์งธ ๋ฌธ์ฅ๋ถํฐ...
// 'Hello Worl' -> 'Hello Wor' -> 'Hello Wo' -> 'Hello W'
}
// (3) ๋ฐ๊ฟ ๋ฌธ์ฅ ์
๋ ฅ
const missingChars = sentence.slice(text.length);
// (์ฒซ๋ฒ์งธ ๋ฌธ์ฅ) 'Hello World'.slice(0) -> 'Hello World'
// (๋๋ฒ์งธ ๋ฌธ์ฅ) 'Hello Winter'.slice(7) -> 'inter'
const missingCharsArray = missingChars.split('');
// (์ฒซ๋ฒ์งธ ๋ฌธ์ฅ) ['H','e','l','l','o',' ','W','o','r','l','d']
// (๋๋ฒ์งธ ๋ฌธ์ฅ) ['i', 'n', 't', 'e', 'r']
for (const missingChar of missingCharsArray) {
text += missingChar;
yield text;
// (์ฒซ๋ฒ์งธ ๋ฌธ์ฅ) 'H' -> 'He' -> 'Hel' -> 'Hell' -> 'Hello' -> ...
// (๋๋ฒ์งธ ๋ฌธ์ฅ) 'Hello Wi' -> 'Hello Win' -> 'Hello Wint' -> ...
}
// (4) ๋ค์ ๋ฌธ์ฅ์ผ๋ก ๋์ด๊ฐ๋ ๋๋ ์ด ์ถ๊ฐ
const delay = 15;
for (let i = 0; i < delay; i++) {
yield text; // ์ค์ ํ ํ์ดํ ์๋๊ฐ 100ms๋ผ๋ฉด 100 * 15 = 1500(1.5์ด)๊ฐ ๋๋ ์ด
// (์ฒซ๋ฒ์งธ ๋ฌธ์ฅ) 'Hello World' 15๋ฒ ๋ ๋
// (๋๋ฒ์งธ ๋ฌธ์ฅ) 'Hello Winter' 15๋ฒ ๋ ๋
}
}
}
import { textGenerator } from '@/lib/utils';
export default function useTypeWriter({
content,
sec = 100,
hasBlink = false,
}) {
const [displayedContent, setDisplayedContent] = useState('');
useEffect(() => {
const generator = textGenerator(content);
const interval = setInterval(() => {
const { value, done } = generator.next();
if (done) {
clearInterval(interval);
} else {
setDisplayedContent(value);
}
}, sec);
return () => clearInterval(interval);
}, []);
return (
<>
{displayedContent}
{hasBlink && <span className="blink" />}
</>
);
}
1๊ฐ ๋ฌธ์ฅ์ด ์๋ ์ฌ๋ฌ๊ฐ์ ๋ฌธ์ฅ์ ๋ฐ๋๋ก ์์ ํ์ผ๋ฏ๋ก ์๋์ฒ๋ผ ๋ฐฐ์ด์ ์ฌ๋ฌ ๋ฌธ์ฅ์ ๋ฃ์ด์ Hook์ ํธ์ถํ๋ค.
// Component.js
import useTypeWriter from '@/hooks/useTypeWriter';
export default function Component() {
const typingText = useTypeWriter({
content: ['Hello World', 'Hello Winter'],
sec: 200,
hasBlink: true,
});
return <p>{typewriterText}</p>;
}
ํ์๊ธฐ ํจ๊ณผ ๋ฐ๋ณตํ๊ธฐ
๊ธฐ๋ณธ์ ์ผ๋ก ์
๋ ฅ๋ฐ์ ๋ชจ๋ ๋ฌธ์ฅ์ ์ํํ๋ฉด ํ์๊ธฐ ํจ๊ณผ๋ ๋ฐ๋ณต์ ๋ฉ์ถ๋ค(๊น๋นก์ด๋ ์ปค์๋ ๊ณ์ ๋ณด์). ๋ง์ฝ ํ์๊ธฐ ํจ๊ณผ๋ฅผ ๋ฐ๋ณตํด์ ๋ณด์ฌ์ฃผ๊ณ ์ถ๋ค๋ฉด(a ๋ฌธ์ฅ → b ๋ฌธ์ฅ → a ๋ฌธ์ฅ → ...) ๊ธฐ์กด ์ฝ๋๋ฅผ while(true)
๋ฌธ์ผ๋ก ๊ฐ์ผ ๋ค ๋ฐ๋ณต ์ฌ๋ถ์ ๋ฐ๋ผ ๋ฉ์ถ์ง, ๊ณ์ํ ์ง์ ๋ํ ์กฐ๊ฑด์ ์ถ๊ฐํ๋ฉด ๋๋ค.
// lib/utils.js
export function* textGenerator(sentences, loop) {
let text = '';
while (true) {
for (const sentence of sentences) {
/* ์๋ต */
}
if (loop === false) {
return;
}
}
}
๋ ํผ๋ฐ์ค
๊ธ ์์ ์ฌํญ์ ๋ ธ์ ํ์ด์ง์ ๊ฐ์ฅ ๋น ๋ฅด๊ฒ ๋ฐ์๋ฉ๋๋ค. ๋งํฌ๋ฅผ ์ฐธ๊ณ ํด ์ฃผ์ธ์
'๐ช Programming' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[JS] ์๋ฐ์คํฌ๋ฆฝํธ ์ ๋๋ ์ดํฐ Generator ์ด ์ ๋ฆฌ (0) | 2024.05.03 |
---|---|
[JS] ์๋ฐ์คํฌ๋ฆฝํธ ์ดํฐ๋ฌ๋ธ Iterable ์ด ์ ๋ฆฌ (0) | 2024.05.03 |
[HTML/CSS] ์์ดํฐ ์ฌํ๋ฆฌ 100vh ์ด์ ํด๊ฒฐ (0) | 2024.05.03 |
[HTML/CSS] ์์ดํฐ ์ฌํ๋ฆฌ ํ ๋ง ์ปฌ๋ฌ ๋ณ๊ฒฝ ๋ฐฉ๋ฒ (0) | 2024.05.03 |
[HTML/CSS] ์ ์ฉํ HTML ํ๊ทธ ๋ชจ์ (0) | 2024.05.03 |
๋๊ธ
์ด ๊ธ ๊ณต์ ํ๊ธฐ
-
๊ตฌ๋
ํ๊ธฐ
๊ตฌ๋ ํ๊ธฐ
-
์นด์นด์คํก
์นด์นด์คํก
-
๋ผ์ธ
๋ผ์ธ
-
ํธ์ํฐ
ํธ์ํฐ
-
Facebook
Facebook
-
์นด์นด์ค์คํ ๋ฆฌ
์นด์นด์ค์คํ ๋ฆฌ
-
๋ฐด๋
๋ฐด๋
-
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
-
Pocket
Pocket
-
Evernote
Evernote
๋ค๋ฅธ ๊ธ
-
[JS] ์๋ฐ์คํฌ๋ฆฝํธ ์ ๋๋ ์ดํฐ Generator ์ด ์ ๋ฆฌ
[JS] ์๋ฐ์คํฌ๋ฆฝํธ ์ ๋๋ ์ดํฐ Generator ์ด ์ ๋ฆฌ
2024.05.03 -
[JS] ์๋ฐ์คํฌ๋ฆฝํธ ์ดํฐ๋ฌ๋ธ Iterable ์ด ์ ๋ฆฌ
[JS] ์๋ฐ์คํฌ๋ฆฝํธ ์ดํฐ๋ฌ๋ธ Iterable ์ด ์ ๋ฆฌ
2024.05.03 -
[HTML/CSS] ์์ดํฐ ์ฌํ๋ฆฌ 100vh ์ด์ ํด๊ฒฐ
[HTML/CSS] ์์ดํฐ ์ฌํ๋ฆฌ 100vh ์ด์ ํด๊ฒฐ
2024.05.03 -
[HTML/CSS] ์์ดํฐ ์ฌํ๋ฆฌ ํ ๋ง ์ปฌ๋ฌ ๋ณ๊ฒฝ ๋ฐฉ๋ฒ
[HTML/CSS] ์์ดํฐ ์ฌํ๋ฆฌ ํ ๋ง ์ปฌ๋ฌ ๋ณ๊ฒฝ ๋ฐฉ๋ฒ
2024.05.03