用 requestanimationframe 实现逐字打字效果最顺滑,因其对齐屏幕刷新率;需清空元素、同步光标、处理换行/空格/html实体;支持暂停/恢复/取消;但长文本、seo、无障碍场景应降级。

用 requestAnimationFrame 实现逐字打字效果,别用 setTimeout 模拟
浏览器重绘节奏不匹配时,setTimeout 容易卡顿或跳字,尤其在页面有其他动画或低性能设备上。requestAnimationFrame 能对齐屏幕刷新率,保证每个字符都在下一帧渲染,视觉更顺滑。
- 每次只插入一个字符,用
textContent或innerHTML更新(避免重复解析 HTML) - 起始前先清空目标元素内容,否则可能叠加显示
- 注意光标闪烁逻辑要和打字节奏同步——别单独用另一个
setInterval,统一用 rAF 控制 - 示例节选:
function typeText(el, str, i = 0) { if (i > str.length) return; el.textContent = str.slice(0, i); requestAnimationFrame(() => typeText(el, str, i + 1)); }
处理换行、空格与 HTML 实体的坑
纯文本逐字输出时,\n 不会换行, 会被当普通字符显示,
<br>会被转义成字符串。这不是“效果没出来”,是 DOM 更新方式错了。
- 如果源字符串含 HTML 标签(如
<br>),改用el.innerHTML = str.slice(0, i),但必须确保内容可信,否则有 XSS 风险 - 想保留换行又不用 HTML?CSS 加
white-space: pre-line,然后把\n当作有效字符参与计数 - 空格连续多个会被浏览器合并,需用
替换,或 CSS 设white-space: pre - 遇到
©这类实体,得先用DOMParser解析,或服务端预转义
暂停/恢复/取消打字的正确姿势
直接用闭包变量控制流程最轻量,别依赖外部状态轮询;取消时记得清掉正在执行的 requestAnimationFrame 句柄。
- 用
let animationId = requestAnimationFrame(...)接收句柄,取消时调cancelAnimationFrame(animationId) - 暂停不是“停住 setTimeout”,而是中断递归调用链,靠一个布尔值
isPaused控制是否继续调度下一次 rAF - 恢复时不要重新开始,而是从当前已输出长度继续,所以要把
i值存为闭包变量或实例属性 - 别在打字中途直接改
el.textContent—— 会破坏状态一致性,后续恢复可能错位
兼容性与性能边界:哪些场景不该硬上打字机
移动端弱网加载文案、长段落首屏渲染、SEO 关键文字,都不适合动态逐字输出。它本质是装饰性动效,不是内容交付手段。
立即学习“前端免费学习笔记(深入)”;
- 搜索引擎不执行 JS,
textContent初始为空 = 该段文字不被索引 - 无障碍阅读器(如 VoiceOver)通常按完整节点播报,逐字更新可能触发多次冗余朗读
- 超过 200 字再做打字效果,用户等待感明显,建议 >100 字就降级为淡入
- Web Worker 里不能用
requestAnimationFrame,也操作不了 DOM —— 别试图把它挪进去











