ch单位是纯css打字机动画唯一可靠单位,因其等于当前字体“0”的宽度,在等宽字体下1字符≈1ch,配合steps(n,end)可严格逐字裁切;而em、%、px易受字体、缩放或响应式影响导致错位。

纯 CSS 实现打字机动画,ch 单位不是“可选项”,而是唯一能避开像素漂移、字体继承干扰和响应式错位的可靠路径。用 em、% 或 px 写 width 动画,90% 的失败都源于单位失准,而不是 timing-function 没调对。
为什么 width 动画必须配 ch 单位 + steps()?
打字效果本质是“逐字符裁切显示”,不是渐变拉伸。浏览器对 width 的插值动画默认平滑过渡,必须用 steps() 强制跳变;而跳变的步长必须严格对齐字符数,这就要求终点宽度能被字符数整除——ch 正是为此设计的单位:它等于当前字体中 "0" 的内联宽度,在等宽字体下,1 个英文/数字/标点 ≈ 1ch,多数中文也接近 1ch(误差可接受)。
-
em受font-size和字符形状影响("i"和"w"宽度差一倍),无法保证每步卡准一个字 -
%依赖父容器宽度,响应式缩放时steps(12)可能只显示前 8 个字 -
px完全脱离字体上下文,换字体或缩放就得重算数值 - 必须写
steps(n, end),漏掉end会变成steps(n, start),第一帧就跳过初始空白状态,导致首字消失
必须显式设置等宽字体,否则 ch 失效
ch 不是魔法单位,它只在字体度量稳定时才“每字一格”。非等宽字体(如 system-ui、Helvetica)下,中文宽度浮动大,1ch 可能只覆盖半个字,动画就会漏字或重叠。
- 强制指定
font-family: 'Fira Code', 'SFMono-Regular', Consolas, monospace - 避免用
font-family: sans-serif或未声明的系统字体 - 若必须混排中英文,建议统一用
font-variant-ligatures: none关闭连字(Firefox 对fi/fl的ch计算异常) - 不要依赖伪元素(如
::after)生成内容再动画——content不支持属性动画,且无法继承ch的上下文宽度
overflow: hidden 和 white-space: nowrap 缺一不可
这两条是裁切逻辑的物理基础。没有 overflow: hidden,width 动画再准也白搭,文字会直接撑开容器;没有 white-space: nowrap,换行会破坏宽度计算,尤其当容器窄于单行文本时,width: 12ch 可能根本达不到预期效果。
-
overflow: hidden必须加在**文字容器本身**,不能只加在父级 -
white-space: nowrap要设在同级容器,否则子元素换行后,width动画只控制第一行可见宽度 - 如果容器有
padding或border,记得加box-sizing: border-box,否则width: 12ch会包含内边距,导致终点偏移 - 别用
max-width替代width——max-width动画不触发重排裁剪,浏览器不会按帧重新计算可见区域
字符数必须精确,动态文本得靠 JS 注入
CSS 无法自动读取文本长度,steps(12, end) 的 12 和 width: 12ch 的 12 必须严格等于 el.textContent.length(含空格、换行符)。静态文本可手算,但真实项目里标题/文案常由 CMS 或 API 注入,这时必须用 JS 补位。
- 用 JS 获取字符数:
const n = el.textContent.length - 注入自定义属性:
el.style.setProperty('--char-count', n) - CSS 中写:
width: calc(var(--char-count) * 1ch)(注意:不能在@keyframes里用calc(),只能用于普通样式声明) - 动画时长建议按字符数动态设定,例如
animation-duration: calc(var(--char-count) * 0.08s),太快看不清,太慢显拖沓 - iOS Safari 对
ch+steps()有跳帧问题,若必须兼容,可拆分 DOM 元素逐字包裹,或降级为 JS 控制opacity或visibility
真正难的不是写出那几行 CSS,而是意识到 ch 不是“另一个单位”,而是把字体度量、动画步进和容器裁切三者绑死的约束条件——少满足一个,效果就断在某个环节。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











