随着2024年国内大模型业务需求的发展,前端领域打字机效果组件已广泛应用于多种网站和应用程序中,有效增强了网站的动态性与交互性。
本文旨在深入探讨如何从基础开始,逐步开发实现简易版的一个打字机效果的流式组件,包括示例代码及其详细解释。
效果

一、初始准备
开始开发前,我们首先需要准备一些基础内容。本次示例中,我们将以一个Markdown文本数据作为打字机效果的内容来源。这不仅能够模拟实际开发中的场景,也便于我们展示如何处理和展示复杂文本。
示例Markdown文本
我们的Markdown文本包含了标题、文本格式(如粗体、斜体)、图片和表格等元素,这些都是Markdown常用的标记元素。通过对这些不同类型的内容进行逐字展示,我们可以演示打字机效果处理复杂文本的能力。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| export const mockMarkdownStr = ` # Markdown 流式文章示例 Markdown是一种轻量级标记语言,它允许人们使用易读易写的纯文本格式编写文档。Markdown编写的文档后缀为 \`.md\`。在这篇文章中,我将向您展示如何使用Markdown创建一篇包含标题、文本、图片和表格的文章。
## 标题 在Markdown中,标题是通过在文字前面加上 \`#\` 来表示的。\`#\` 的数量代表标题的级别。例如,一个 \`#\` 代表一级标题,两个 \`##\` 代表二级标题,以此类推。
## 文本 Markdown支持普通的文本格式,如粗体、斜体、删除线和下划线。您可以使用以下方式创建这些文本格式: - **粗体**:使用两个 \`\*\` 或 \`_\` 包围文本,例如 \`\*\*粗体\*\*\` 或 \`__粗体__\` - *斜体*:使用一个 \`\*\` 或 \`_\` 包围文本,例如 \`\*斜体\*\` 或 \`_斜体_\` - ~~删除线~~:使用两个 \`~\` 包围文本,例如 \`~~删除线~~\` - <u>下划线</u>:使用HTML标签 \`<u>\` 和 \`</u>\` 包围文本,例如 \`<u>下划线</u>\`
## 图片 在Markdown中,您可以使用以下语法插入图片: \`\`\`  \`\`\` 例如: \`\`\`  \`\`\`
## 表格 Markdown支持简单的表格创建。您可以使用以下语法创建表格: \`\`\` | 标题1 | 标题2 | 标题3 | |-------|-------|-------| | 单元格1 | 单元格2 | 单元格3 | | 单元格4 | 单元格5 | 单元格6 | \`\`\` 例如: \`\`\` | 姓名 | 年龄 | 性别 | |------|------|------| | 张三 | 25 | 男 | | 李四 | 22 | 女 | \`\`\`
## 结束语 以上是Markdown的基本用法,通过这些简单的标记,您可以创建一篇包含标题、文本、图片和表格的文章。Markdown的语法简单易懂,适合快速排版和分享文档。希望这篇文章对您有所帮助! `;
|
二、代码实现
实现打字机效果涉及三个部分:
- 核心逻辑
TypeWriterCore
- Hook封装
useTypeWriter
- React组件实现
TypeWriter Components
三个部分业务侧可按需取用,以下部分将详细介绍每一部分的实现逻辑及关键代码。
1. TypeWriterCore.ts
- 打字机核心逻辑
TypeWriterCore.ts
文件中定义了TypeWriterCore
类,这个类封装了打字机效果的核心逻辑。通过构造函数,我们可以传入不同的配置选项,如打字速度、暂停时间等,以适应不同的使用场景。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| interface TypeWriterCoreOptions { onConsume: (str: string) => void; maxStepSeconds?: number; }
export default class TypeWriterCore { onConsume: (str: string) => void; queueList: string[] = []; maxStepSeconds: number = 50; maxQueueNum: number = 2000; timer: number | undefined;
constructor({onConsume, maxStepSeconds}: TypeWriterCoreOptions) { this.onConsume = onConsume;
if (maxStepSeconds !== undefined) { this.maxStepSeconds = maxStepSeconds; } }
dynamicSpeed() { const speedQueueNum = this.maxQueueNum / this.queueList.length; const resNum = +( speedQueueNum > this.maxStepSeconds ? this.maxStepSeconds : speedQueueNum ).toFixed(0);
return resNum; }
onAddQueueList(str: string) { this.queueList = [...this.queueList, ...str.split('')]; }
add(str: string) { if (!str) return; this.onAddQueueList(str); }
consume() { if (this.queueList.length > 0) { const str = this.queueList.shift(); str && this.onConsume(str); } }
next() { this.timer = setTimeout(() => { if (this.queueList.length > 0) { this.consume(); this.next(); } }, this.dynamicSpeed()); }
start() { this.next(); }
onRendered() { clearTimeout(this.timer); }
onClearQueueList() { this.queueList = []; clearTimeout(this.timer); } }
|
2. useTypeWriter.ts
- Hook封装
通过HookuseTypeWriter
封装TypeWriterCore
类,提供简洁的接口,使得在React或Vue组件中易于实现打字机效果。
代码示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| import {useEffect, useState, useMemo} from 'react'; import TypeWriterCore from './TypeWriterCore';
interface UseWriterOptions { maxStepSeconds?: number; }
export const useTypeWriter = ( {text, options}: { text: string, options?: UseWriterOptions } ) => { const [typedText, setTypedText] = useState('');
const typingCore = useMemo( () => new TypeWriterCore( { onConsume: (str: string) => setTypedText(prev => prev + str), ...options, } ), [] );
useEffect( () => { typingCore.onRendered(); typingCore.add(text); typingCore.start();
return () => typingCore.onRendered(); }, [text] );
return [typedText]; };
|
3. index.tsx
- 组件实现示例
这个文件展示了如何在React组件中使用useTypeWriter
Hook来实现打字机效果。以下是实现的关键部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| import React from 'react'; import ReactMarkdown from 'react-markdown'; import {useTypeWriter} from './useTypeWriter'; import TypeWriterCore from './TypeWriterCore';
interface TypingWriterProps { text: string; options?: { maxStepSeconds?: number; }; }
const TypingWriter: React.FC<TypingWriterProps> = ({text, options = {}}) => { const [typedText] = useTypeWriter({text, options});
return ( <div> <ReactMarkdown> {typedText} </ReactMarkdown> </div> ); };
export { TypingWriter, TypeWriterCore, useTypeWriter, };
|
通过这三个文件的详细解析和代码实现,我们展示了从核心逻辑的构建到在React组件中的应用,如何逐步开发一个打字机效果的流式组件。
三、应用示例
1、模拟流式文本消息推送:
以下示例展示如何模拟SSE(Server-Sent Events)文本消息推送,模拟实时数据流的场景。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| export const simulateWebSocketPush = (text, onDataReceived) => { const words = text.split(/([\s,。;:!、])/); let currentIndex = 0;
function pushNextChunk() { const chunkSize = Math.floor(Math.random() * 5) + 1; const currentChunk = words.slice(currentIndex, currentIndex + chunkSize).join(''); currentIndex += chunkSize;
onDataReceived(currentChunk);
if (currentIndex < words.length) { const interval = Math.floor(Math.random() * 500) + 1000; setTimeout(pushNextChunk, interval); } }
pushNextChunk(); };
|
2. 使用示例React
此示例展示如何在应用中模拟文本数据传入,实现打字机效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import {useEffect, useState} from 'react'; import {TypingWriter} from 'ui-type-writer'; import {simulateWebSocketPush} from '@/utils'; import {mockMarkdownStr} from '@/mock/index'
export default function App() { const [markdownContent, setMarkdownContent] = useState(' ');
useEffect( () => { simulateWebSocketPush(mockMarkdownStr, data => { setMarkdownContent(data); }); return () => {}; }, [] );
return ( <TypingWriter text={markdownContent} /> ); }
|
打字机效果组件已发布到npm,未进行预编译,可以在目录下直接调试。
1
| npm install ui-type-writer
|
希望本文能为您提供有价值的参考与指导,如有疑问或建议,敬请留言讨论。