Table of Contents

1、概述

这是一个基于 useEffect 实现的打字效果演示

预览

We

实现思路如下

  1. 首先,注意观察,我们这里有多个单词需要依次显示,因此,我们需要 2 个 index 的状态,一个用来记录当前显示的是第几个单词,一个用来记录当前显示的是第几个字母
  2. 然后,我们需要一个状态来记录当前是否是反向显示,因为我们需要在单词显示完毕后,进行反向显示,然后再显示下一个单词
  3. 最后,我们需要一个状态来记录当前是否是闪烁,因为我们需要在单词显示完毕后,进行闪烁,然后再显示下一个单词
index.tsx
1
const [index, setIndex] = useState(0)
2
const [subIndex, setSubIndex] = useState(0)
3
const [reverse, setReverse] = useState(false)
4
const [blink, setBlink] = useState(true)

其次,光标闪烁是一个持续行为,因此,我们可以通过一个 interval 来实现,每次执行时,我们只需要切换一下 blink 状态即可

index.tsx
1
useEffect(() => {
2
const blinkTimer = setInterval(() => {
3
setBlink((prev) => !prev)
4
}, 500)
5
return () => clearInterval(blinkTimer)
6
}, [])

然后,打字效果是一个往下执行的过程,在这个过程中,index 和 subIndex 会递增或者递减,并且他们每次变化之后,还需要触发下一次的执行,因此,我们可以共同将 index subIndex reverse 三个状态的变化,都放到一个 useEffect 的依赖项去

最终完整的代码试下如下所示

index.tsx
01
02
import { useState, useEffect } from 'react'
03
04
const WORDS = ["Design.", "Develop.", "Ship.", "Innovate."]
05
06
export default function Typewriter() {
07
const [index, setIndex] = useState(0)
08
const [subIndex, setSubIndex] = useState(0)
09
const [reverse, setReverse] = useState(false)
10
const [blink, setBlink] = useState(true)
11
12
// 逻辑:控制光标闪烁
13
useEffect(() => {
14
const blinkTimer = setInterval(() => {
15
setBlink((prev) => !prev)
16
}, 500)
17
return () => clearInterval(blinkTimer)
18
}, [])
19
20
// 逻辑:控制打字
21
useEffect(() => {
22
if (subIndex === WORDS[index].length + 1 && !reverse) {
23
setReverse(true)
24
return
25
}
26
27
if (subIndex === 0 && reverse) {
28
setReverse(false)
29
setIndex((prev) => (prev + 1) % WORDS.length)
30
return
31
}
32
33
const timeout = setTimeout(() => {
34
setSubIndex((prev) => prev + (reverse ? -1 : 1))
35
}, Math.max(reverse ? 75 : subIndex === WORDS[index].length ? 1000 : 150, parseInt(Math.random() * 350 as any)))
36
37
return () => clearTimeout(timeout)
38
}, [subIndex, index, reverse])
39
40
return (
41
<div className="flex m-4 h-32 items-center justify-center bg-linear-to-br from-indigo-900 to-slate-900 shadow-inner">
42
<h1 className="text-4xl font-bold text-white tracking-tight">
43
We{' '}
44
<span className="bg-clip-text text-transparent bg-linear-to-r from-blue-400 to-purple-400">
45
{WORDS[index].substring(0, subIndex)}
46
</span>
47
<span className={`${blink ? 'opacity-100' : 'opacity-0'} ml-1 inline-block h-8 w-1 bg-purple-400 align-middle transition-opacity`}></span>
48
</h1>
49
</div>
50
)
51
}
专栏首页
到顶
专栏目录