必须用 ch 单位实现打字机动画,因其与字符宽度强绑定;steps(N, end) 中 N 必须等于字符数,配合 overflow: hidden、white-space: nowrap 等样式才能严丝合缝对齐。

直接用 width 动画配 steps() 做打字机,90% 的翻车点不在写法,而在单位选错——em、rem、% 都会漂,只有 ch 能让每步真正对齐一个字符宽度(前提是字体度量合理)。
为什么必须用 ch 单位而不是 % 或 em
ch 表示当前字体中 “0” 字符的内联尺寸,它和字符实际渲染宽度强绑定;而 % 依赖父容器宽度,em 受 font-size 和继承链干扰,一换字体或缩放就错位。中英文混排时,ch 虽不完美(比如某些中文字宽 ≈ 2ch),但仍是目前 CSS 原生方案里最可控的单位。
-
width: 0→width: 12ch才能确保第 12 步刚好显示全部 12 个字符(含空格) - 若硬写
width: 100%,动画终点取决于父容器,文字可能提前撑满或永远显不全 - 不要在
@keyframes里写calc(var(--n) * 1ch),CSS 动画不支持动态 calc,得靠 JS 注入自定义属性或内联 style
steps(n, end) 和 step-end 的区别与选用
steps(12, end) 明确告诉浏览器:把整个动画切成 12 步,每步结束时才跳变一次;step-end 是它的简写形式,等价于 steps(1, end) ——但仅适用于光标闪烁这类单步动画。打字主动画必须写全 steps(N, end),N 必须等于字符数。
- 写成
steps(12)(缺第二个参数):部分浏览器 fallback 到start,首字符延迟一帧才出现 - 写成
steps(12, start):第一帧就显示第一个字符,但最后一帧为空白,视觉上像“少打一个字” - 光标动画可放心用
step-end,因为只关心“开/关”两种状态,不需要分 N 步
必须加的样式组合,缺一不可
光有 ch 和 steps() 不够,容器行为必须锁死,否则流式布局会让动画失效。
-
overflow: hidden必须作用于文字容器本身,不是父级——否则裁剪无效 -
white-space: nowrap防止空格或连字符触发换行,破坏宽度节奏 - 容器需是
inline-block或inline-flex,否则width动画对inline元素无效 -
border-right光标要设在同一个元素上,且动画 timing function 必须和打字动画一致,否则脱节
中文和 Emoji 场景下的精度妥协
ch 在非等宽字体下对中文/Emoji 的估算天然有偏差,这不是 bug,是字体度量限制。例如“我”和“0”宽度不同,“??”可能占多行空间。
- 优先用等宽中文字体栈:
font-family: "Fira Code", "Sarasa Gothic SC", monospace - 若必须用常规中文字体,可用 JS 测出平均字符宽度:
getBoundingClientRect().width / el.textContent.length,再转为像素值注入 - Emoji 多时建议改用
clip-path: inset(0 100% 0 0)+steps(),它按区域裁剪,不受字符宽度影响
真正难的不是写出第一版效果,而是当设计师临时加一个顿号、运营改一句文案、用户用 Safari 缩放页面时,动画还能严丝合缝对上每个字——这时候你才会发现,ch 不是银弹,而是所有妥协里最不掉链子的那个。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











