【大前端攻城狮之路】用 Typewriter-SSE 实现打字机效果

在现代 Web 开发中,实现动态文本渲染的需求日益增多。无论是聊天应用、实时通知,还是交互式界面,打字机风格的文本渲染都能显著提升用户体验。最近新写了一个开源的 NPM 包——Typewriter-SSE,它通过 Server-Sent Events (SSE) 技术实现了流式文本传输和打字机效果渲染。项目代码已开源,可在 GitHub 查看。效果见:

一、SSE 技术简介

Server-Sent Events (SSE) 是一种允许服务器向浏览器单向推送数据的技术。与传统的轮询或 WebSocket 不同,SSE 基于 HTTP 协议,仅支持文本数据传输,且服务器到客户端的连接保持开启状态,直到服务器关闭连接。SSE 的主要特点包括:
  1. 单向通信:仅支持服务器向客户端推送数据,适合不需要双向通信的场景。
  2. 轻量级:基于 HTTP 协议,无需额外的 WebSocket 协议支持。
  3. 自动重连:浏览器会自动处理连接中断后的重连逻辑。
  4. 简单易用:通过 EventSource 接口即可在客户端实现 SSE 的接收。
SSE 的典型应用场景包括实时通知、动态数据更新等,尤其适合需要服务器主动推送数据的场景。
 

Typewriter-SSE 的实现原理

Typewriter-SSE 结合了 SSE 技术和前端动画,实现了流式文本的打字机效果渲染。其核心逻辑包括:
  1. SSE 数据接收:通过 EventSource 接口连接到服务器端的 SSE 端点,接收服务器推送的文本数据。
  2. 逐字渲染:将接收到的文本数据逐字渲染到指定的容器中,通过定时器控制每个字符的渲染速度,模拟打字机效果。
  3. 光标动画:通过 CSS 动画实现光标的闪烁效果,增强视觉体验。
  4. 交互控制:提供暂停、恢复、跳过和清空等控制方法,允许用户在渲染过程中进行操作

二、如何安装和使用 Typewriter-SSE?

安装 Typewriter-SSE 非常简单,只需要通过 npm 命令即可完成:
?
1
npm install typewriter-sse 

 

在项目中使用

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import TypewriterSSE from 'typewriter-sse' ;
 
const writer = new TypewriterSSE({
   container: '#output' , // 指定文本渲染的容器
   endpoint: '/sse?q=hello world' , // SSE 服务端的 URL
   cursor: {
     blink: true , // 是否让光标闪烁
     color: '#0f0' , // 光标颜色
     char: '|' // 光标字符
   },
   onComplete: () => console.log( 'Finished typing!' ) // 文本渲染完成后的回调函数
});
 
writer.start();

  

三、主要功能和配置选项

1. 打字机效果

Typewriter-SSE 的核心功能是将文本逐字渲染到指定的容器中,模拟打字机的效果。你可以通过 typingSpeed 选项来控制每个字符的延迟时间,从而调整打字的速度。

2. SSE 支持

通过 SSE 技术,Typewriter-SSE 能够动态接收来自服务器的文本数据。这意味着你可以将文本分段发送到客户端,而客户端会逐字渲染这些文本。这种技术特别适合实现聊天机器人、实时通知等场景。

3. 光标自定义

你可以通过 cursor 选项来自定义光标的样式,包括是否闪烁、颜色和字符。这让你可以根据项目的整体风格来调整光标的视觉效果。

4. 事件回调

Typewriter-SSE 提供了多种事件回调函数,例如 onChar(每个字符渲染时触发)和 onComplete(文本渲染完成时触发)。这些回调函数可以帮助你更好地控制文本渲染过程,并在合适的时候执行其他逻辑。

5. 暂停、恢复、跳过和清空

Typewriter-SSE 提供了暂停、恢复、跳过和清空文本的控制方法。你可以通过这些方法来实现更复杂的交互逻辑,例如让用户暂停文本渲染、跳过未渲染的文本或清空已渲染的文本。
 
 

四、一个完整的示例

为了让大家更好地理解 Typewriter-SSE 的使用方法,我将展示一个完整的示例。这个示例包括一个简单的服务器端代码和一个客户端页面,用于演示 Typewriter-SSE 的效果。

1. 服务器端代码

这是一个基于 Node.js 的简单服务器代码,它通过 SSE 技术向客户端发送分段的文本数据。
?
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
const express = require( 'express' );
const path = require( 'path' );
const app = express();
const PORT = 3000;
 
app.use(express. static (path.join(__dirname, 'public' )));
 
app.get( '/sse' , (req, res) => {
     res.setHeader( 'Content-Type' , 'text/event-stream' );
     res.setHeader( 'Cache-Control' , 'no-cache' );
     res.setHeader( 'Connection' , 'keep-alive' );
 
     const query = req.query.q || "你没有输入任何内容哦~" ;
 
     const paragraphs = [
         `你输入的是「${query}」`,
         `这是段模拟的第一个回复 🌟`,
         `接下来是第二段回复 🚀`,
         `最后一段啦,演示完毕 🏁`
     ];
 
     let pIndex = 0;
     let charIndex = 0;
 
     const interval = setInterval(() => {
         if (pIndex < paragraphs.length) {
             if (charIndex < paragraphs[pIndex].length) {
                 res.write(`data: ${paragraphs[pIndex][charIndex++]}\n\n`);
             } else {
                 res.write(`data: \n\n`); // 空行分段
                 pIndex++;
                 charIndex = 0;
             }
         } else {
             clearInterval(interval);
             res.write( 'event: done\n' );
             res.write( 'data: end\n\n' );
             res.end();
         }
     }, 50);
});
 
app.listen(PORT, () => {
     console.log(`Server running at http: //localhost:${PORT}`);
});

  

2. 客户端页面

这是一个简单的 HTML 页面,它使用 Typewriter-SSE 来渲染来自服务器的文本数据。
?
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
77
78
79
80
81
82
83
84
85
<!DOCTYPE html>
<html lang= "zh" >
<head>
     <meta charset= "UTF-8" >
     <title>打字机 + SSE + 控制面板</title>
     <style>
         body {
             background: #111;
             color: #0f0;
             font-family: monospace;
             padding: 20px;
         }
         #output {
             white-space: pre-wrap;
             font-size: 1.2em;
             min-height: 100px;
             margin-top: 1em;
         }
         input[type= "text" ], button {
             padding: 8px;
             font-size: 1em;
             margin: 5px 5px 5px 0;
             background: #222;
             color: #0f0;
             border: 1px solid #0f0;
         }
         .controls {
             margin-top: 10px;
         }
     </style>
</head>
<body>
 
     <h2>打字机效果演示</h2>
     <input type= "text" id= "prompt" placeholder= "请输入..." />
     <div class = "controls" >
         <button id= "send" >发送</button>
         <button id= "pause" >暂停</button>
         <button id= "resume" >继续</button>
         <button id= "skip" >跳过</button>
         <button id= "delete" >清空输出</button>
     </div>
     <div id= "output" ></div>
 
     <script type= "module" >
         import TypewriterSSE from './typewriter-sse.js' ;
 
         let writer = null ;
 
         document.getElementById( 'send' ).addEventListener( 'click' , () => {
             const query = document.getElementById( 'prompt' ).value.trim();
             if (!query) return alert( '请输入内容' );
             if (writer) writer.stop();
 
             const output = document.getElementById( 'output' );
             output.textContent = "" ;
 
             writer = new TypewriterSSE({
                 container: output,
                 endpoint: '/sse?q=' + encodeURIComponent(query),
                 cursor: { blink: true , color: '#0f0' , char: '|' },
                 onComplete: () => console.log( '输入完成' )
             });
             writer.start();
         });
 
         document.getElementById( 'pause' ).addEventListener( 'click' , () => {
             if (writer) writer.pause();
         });
 
         document.getElementById( 'resume' ).addEventListener( 'click' , () => {
             if (writer) writer.resume();
         });
 
         document.getElementById( 'skip' ).addEventListener( 'click' , () => {
             if (writer) writer.skip();
         });
 
         document.getElementById( 'delete' ).addEventListener( 'click' , () => {
             if (writer) writer.deleteAll();
         });
     </script>
 
</body>
</html>

  

总结

Typewriter-SSE 是一个基于 SSE 技术实现的流式文本渲染库,能够为 Web 应用带来动态的打字机效果。它不仅支持流式文本传输,还提供了丰富的交互控制功能。如果你对 Typewriter-SSE 感兴趣,或者希望了解更多实现细节,欢迎访问 GitHub 仓库

 

原创作者: tjyoung 转载于: https://www.cnblogs.com/tjyoung/p/18853887
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值