steps()函数本质是帧切分而非时间控制,需严格匹配字符数(含空格)设steps(n,end),配合ch单位与width实现精准打字效果。

steps() 函数的本质是“帧切分”,不是时间控制
steps() 不是让动画变快或变慢,而是把整个动画过程硬切成若干等份、每份“跳”一次。打字效果依赖的正是这种“逐字符出现”的跳跃感——它和 ease 或 linear 这类平滑过渡函数逻辑相反。
常见错误是写成 animation: typing 3s steps(10, end) 却没匹配文字长度:如果文本只有 5 个字符,却设 steps(10),后半段就会空跳;设 steps(3) 又会漏掉字符。关键在步数必须等于字符数(含空格)。
- 用
steps(n, end):每个 step 在区间末尾生效,更符合“新字符突然出现”的直觉 - 用
steps(n, start):第一帧就显示全部,之后才开始“回退式”消失,不适合打字 - 中文、emoji、全角空格都算 1 个字符,
"你好 "是 3 步,不是 2 步
核心技巧:用 ch 单位 + width 配合 steps()
打字效果本质是“从 0 宽度逐渐展开到完整宽度”,所以不能靠 opacity 或 transform 模拟——那些无法精准对齐字符边界。正确做法是用单行内联元素(如 <span></span>)配合 overflow: hidden 和动态 width,再用 steps() 控制宽度增长节奏。
示例 HTML:
立即学习“前端免费学习笔记(深入)”;
<p class="typing"><span>Hello world</span></p>
CSS 关键部分:
.typing span {
display: inline-block;
overflow: hidden;
width: 0;
animation: typing 2.5s steps(11, end), blink-caret .75s step-end infinite;
}
@keyframes typing {
from { width: 0 }
to { width: 11ch } /* 必须和 steps(11) 中的数字一致 */
}-
11ch表示 11 个“0”字符的宽度,对等宽字体(如monospace)最准;非等宽字体下会有轻微错位,但肉眼通常不敏感 - 必须显式设置
font-family: monospace,否则中英文混排时ch单位会失准 -
step-end是steps(1, end)的简写,专用于光标闪烁这类单步切换
兼容性与真实场景的取舍点
steps() 在所有现代浏览器中都支持,包括 iOS Safari 12+ 和 Android Chrome 60+,但老版本微信内置浏览器(X5 内核)对 ch 单位支持不稳定,可能表现为宽度卡顿或跳过。
- 降级方案:用 JavaScript 动态生成
<span></span>包裹每个字符,再逐个opacity: 1,牺牲 CSS 动画性能换兼容性 - 响应式陷阱:如果容器缩放(如
transform: scale(0.8)),ch单位不会自动缩放,需同步调整width值或改用em+ 字体度量估算 - 不要用
steps()做长段落滚动打字——超过 50 字符时,CSS 动画帧率易下降,人眼会感知卡顿
容易被忽略的细节:空格、换行与伪元素光标
很多人复制代码后发现光标不闪,或开头多出一个空格,问题往往出在 HTML 空白符处理或伪元素定位上。
-
<span> Hello </span>里首尾空格会被渲染为字符,计入steps()步数,应写成<span>Hello</span>并用 CSS 控制外边距 - 光标用
::after实现时,若父元素line-height不一致,光标会上下偏移;稳妥做法是给span设line-height: 1,再单独调::after的line-height - 换行文本想实现“逐行打字”,不能只靠一个
steps():需用多个<span></span>分行,每行独立动画,延迟用animation-delay错开
真正难的不是写出第一版效果,而是让不同字体、不同设备、不同内容长度下都保持字符对齐和节奏稳定——这需要反复测 ch 值和手动微调。


















