共计 1831 个字符,预计需要花费 5 分钟才能阅读完成。
传统方案的痛点
以前实现打字机效果,最常用的方法是 setInterval,但实际用过的同学应该都遇到过这些问题:

- 卡顿明显 :特别是在低端设备上,间隔时间很难调到完美
- CPU 占用高 :持续运行的定时器会一直占用主线程
- 控制困难 :暂停 / 继续逻辑需要额外维护状态变量
// 典型问题代码示例
let i = 0;
const timer = setInterval(() => {element.textContent = text.slice(0, i++);
if (i > text.length) clearInterval(timer);
}, 100); // 固定间隔不跟帧率同步
技术方案选型
主流方案对比
- SSE(Server-Sent Events)
- 适合服务端推送场景
- 需要后端配合
-
自动重连机制
-
WebSocket
- 全双工通信
- 适合高频更新场景
-
实现复杂度较高
-
Generator 方案 (推荐)
- 纯前端实现
- 资源消耗低
- 完美配合动画帧
核心实现三步走
1. Generator 文本分块
function* textGenerator(text, chunkSize = 1) {for (let i = 0; i < text.length; i += chunkSize) {yield text.slice(0, i + chunkSize);
}
return text; // 最终返回完整文本
}
2. 动画帧驱动渲染
const renderText = async (element, generator) => {
let done = false;
const renderFrame = () => {const { value, done: genDone} = generator.next();
element.textContent = value;
done = genDone;
if (!done) {requestAnimationFrame(renderFrame);
}
};
requestAnimationFrame(renderFrame);
};
3. 实现播放控制
class TypeWriter {constructor(element) {
this.element = element;
this.generator = null;
this.isPlaying = false;
}
play(text) {this.generator = textGenerator(text);
this.isPlaying = true;
this._render();}
pause() {this.isPlaying = false;}
_render() {if (!this.isPlaying) return;
const {value, done} = this.generator.next();
this.element.textContent = value;
if (!done) {requestAnimationFrame(() => this._render());
}
}
}
进阶优化技巧
Web Worker 分流
对于超长文本(10 万 + 字符),可以将文本分块逻辑放到 Worker 中:
// worker.js
self.onmessage = ({data}) => {const chunked = [];
for (let i = 0; i < data.text.length; i += data.chunkSize) {chunked.push(data.text.slice(0, i + data.chunkSize));
}
postMessage(chunked);
};
性能优化数据
测试环境:MacBook Pro M1 / Chrome 120
| 方案 | 10K 字符耗时 | 内存占用 |
|---|---|---|
| setInterval | 3.2s | 85MB |
| Generator | 1.8s | 42MB |
| Generator+Worker | 1.5s | 38MB |
常见问题解决
内存泄漏预防
- 及时清除 Generator 引用
- 长文本分页渲染
- 使用 WeakMap 管理 DOM 引用
移动端适配
/* 禁止文本选中和长按菜单 */
.typewriter {
user-select: none;
-webkit-touch-callout: none;
}
动手实践
完整示例已上传 CodeSandbox:
在线演示链接
思考题:如果要渲染中英文混合文本,如何保证不同字符的显示速度一致?欢迎在评论区分享你的方案!
总结
通过 Generator+requestAnimationFrame 的方案,我们实现了:
– 60fps 流畅动画
– 极低的内存占用
– 灵活的控制能力
这套方案已经在我们产品的实时日志展示模块稳定运行半年,日均渲染超 200 万字符无异常。希望对你有所启发!
正文完
发表至: 前端开发
近一天内
