纯CSS打字机效果需用animation+steps()控制width或clip-path,配合overflow:hidden和border-right光标;steps()确保逐字离散显示,n必须等于字符数(含空格标点),中文/emoji需注意UTF-16码元差异。

直接说结论:用 animation + steps() 控制 width 或 clip-path,配合 overflow: hidden 和光标闪烁,就能实现纯CSS打字机效果——不需要JS,但必须手动或动态算对步数。
为什么用 steps() 而不是 ease?
因为打字是离散行为:一个字、一个字、一个字地出现,不是平滑过渡。用 ease 或 linear 会让宽度连续增长,文字会“拉伸”或“模糊浮现”,完全不像打字。而 steps(20, end) 表示把整个动画切成 20 步,每步跳到下一个字符位置,视觉上就是逐字点亮。
-
steps(n, end)是最常用写法,表示在每步**结束时**更新样式(即第1步显示第1个字,第2步显示前2个字……) -
steps(n, start)则是在每步**开始时**更新,首帧就会显示第一个字,容易漏掉“起始空白”感 - n 必须等于文本总字符数(含空格、标点),否则最后一两个字可能卡住或跳过
width 和 clip-path 两种实现方式怎么选?
两者都能隐藏/显露文字,但行为逻辑不同,影响兼容性和维护性:
- 用
width:需设white-space: nowrap防换行,overflow: hidden截断,再动画width: 0 → 100%。优点是直觉强、IE11+ 支持好;缺点是响应式下 100% 宽度可能不准(尤其字体变化、缩放时) - 用
clip-path: inset(0 100% 0 0):从右往左“擦除遮罩”,不依赖容器宽度,响应式更稳;但 Safari 旧版本(≤15.4)对inset()动画支持不稳定,且不能用百分比值做插值(得用具体 px/em 值) - 现代项目推荐
clip-path方案,但若需兼容 iOS 15 以下设备,退回width更稳妥
光标闪烁为什么用 border-right 而不是伪元素?
因为要和打字节奏同步:光标必须停在最后一个已显示字符右侧。用 border-right 直接附着在文本容器上,天然对齐;而 ::after 伪元素需要精确控制 left 或 transform,一旦文本换行、字体加载延迟、或使用了 ch 单位,位置就容易偏移。
立即学习“前端免费学习笔记(深入)”;
- 闪烁动画本身用
step-end模式,避免闪烁频率被重绘打断 - 别用
opacity动画做光标——透明度变化会触发合成层重建,低端设备易掉帧 - 最后一行才加光标:多行文本时,只给最后一个
.typing-line元素应用blink-caret动画
最容易被忽略的坑:字符数 ≠ 可见字数
中文、emoji、全角符号、零宽空格、连字(ligature)都可能让 steps(n) 的 n 失准。比如 “??” 是一个 emoji 但占多个 UTF-16 码元;“fi” 在某些字体里会合为一个连字形。浏览器计算 steps() 时按 DOM 文本节点原始长度,不是渲染后字形数。
- 调试时先用固定英文字符串(如
"Hello world!")验证逻辑,再换真实文案 - 动态内容建议用 JS 预读
textContent.length,注入 CSS 变量(如--char-count: 12),再在 animation 中写steps(var(--char-count)) - 如果用了 Web Font,确保
@font-face加载完成后再触发动画,否则字体加载延迟会导致 width 计算错位


















