用 ch 单位 + step-end 实现打字机效果最可靠,因 width 动画无法逐字裁切且受混排、字体度量影响;需 overflow: hidden、width: nch、steps(n, end) 或 step-end,并同步控制光标动画。

animation + width 实现打字机效果为什么通常失败
直接用 width 动画模拟打字,几乎必然出问题:文字是流式布局,width 变化不会“逐字裁切”,而是拉伸或压缩整个容器;更关键的是,中英文混排、换行、字体度量差异会让 width 值极难精确控制。这不是参数调得不够细,而是思路错了。
真正可靠的打字机效果必须用 ch 单位 + step-end
核心是让动画帧严格对齐字符数,不依赖像素宽度。用 ch(一个“0”的显示宽度)作为 width 单位,配合 steps() 时间函数,才能实现“每帧只显示一个字符”的机械感。
实操要点:
-
overflow: hidden必须加在容器上,否则超出部分会溢出 - 动画的
to值写成width: nch,其中 n 是文本总字符数(含空格),可用 JS 动态算:el.textContent.length - 必须用
animation-timing-function: step-end,写成steps(n, end)也行,但step-end更简洁且兼容性更好 - 避免用
em或rem,它们受字体大小继承影响,ch才与当前字体实际字符宽度绑定
示例 CSS:
code{.typewriter {
overflow: hidden;
border-right: 1px solid;
white-space: nowrap;
animation: typing 3s steps(12, end), blink-caret .75s step-end infinite;
}}
@keyframes typing {
from { width: 0 }
to { width: 12ch }
}
@keyframes blink-caret {
from, to { border-color: transparent }
50% { border-color: currentColor }
}
中文、Emoji 和等宽字体不是必须的,但会影响 ch 精度
ch 在非等宽字体下,对中文和 Emoji 的宽度估算可能偏差——比如某些中文字宽度接近 2ch,而 Emoji 可能撑满整行。这不是 bug,是字体度量本身的限制。
应对方式:
- 若内容确定为纯 ASCII(如命令行提示、代码片段),放心用
ch - 含中文时,优先选等宽中文字体(如
Consolas, "Microsoft YaHei", monospace),并测试几个典型词的ch值是否稳定 - 完全不可控的富文本场景(如用户输入含各种 Emoji),改用 JS 驱动的字符级
opacity或clip-path更稳妥
光标闪烁和动画暂停容易被忽略的细节
纯 CSS 打字机最常漏掉两件事:光标没同步停、动画中途暂停后恢复错乱。
关键处理:
- 光标动画(
blink-caret)必须独立声明,且 duration 要比打字动画短,否则会卡顿 - 如果需要暂停,不要只暂停
typing动画——blink-caret也要一起暂停,否则光标会继续闪 - 用
animation-play-state: paused控制时,确保两个动画名都在同一选择器里声明,否则无法统一控制 - 暂停后恢复,
steps()不会重置帧序号,所以不用额外处理计数逻辑
复杂点在于:当文本动态更新(比如 Vue 的响应式文本),CSS 动画不会自动重播,必须手动触发动画重置——这已经超出纯 CSS 范围了。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











