这是一个基于 useEffect 实现的打字效果演示
实现思路如下
1const [index, setIndex] = useState(0)2const [subIndex, setSubIndex] = useState(0)3const [reverse, setReverse] = useState(false)4const [blink, setBlink] = useState(true)
其次,光标闪烁是一个持续行为,因此,我们可以通过一个 interval 来实现,每次执行时,我们只需要切换一下 blink 状态即可
1useEffect(() => {2const blinkTimer = setInterval(() => {3setBlink((prev) => !prev)4}, 500)5return () => clearInterval(blinkTimer)6}, [])
然后,打字效果是一个往下执行的过程,在这个过程中,index 和 subIndex 会递增或者递减,并且他们每次变化之后,还需要触发下一次的执行,因此,我们可以共同将 index subIndex reverse 三个状态的变化,都放到一个 useEffect 的依赖项去
最终完整的代码试下如下所示
0102import { useState, useEffect } from 'react'0304const WORDS = ["Design.", "Develop.", "Ship.", "Innovate."]0506export default function Typewriter() {07const [index, setIndex] = useState(0)08const [subIndex, setSubIndex] = useState(0)09const [reverse, setReverse] = useState(false)10const [blink, setBlink] = useState(true)1112// 逻辑:控制光标闪烁13useEffect(() => {14const blinkTimer = setInterval(() => {15setBlink((prev) => !prev)16}, 500)17return () => clearInterval(blinkTimer)18}, [])1920// 逻辑:控制打字21useEffect(() => {22if (subIndex === WORDS[index].length + 1 && !reverse) {23setReverse(true)24return25}2627if (subIndex === 0 && reverse) {28setReverse(false)29setIndex((prev) => (prev + 1) % WORDS.length)30return31}3233const timeout = setTimeout(() => {34setSubIndex((prev) => prev + (reverse ? -1 : 1))35}, Math.max(reverse ? 75 : subIndex === WORDS[index].length ? 1000 : 150, parseInt(Math.random() * 350 as any)))3637return () => clearTimeout(timeout)38}, [subIndex, index, reverse])3940return (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">43We{' '}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}