先记住这个答案
steps() 和 linear 是两种完全不同的缓动方式。linear 让属性值随时间平滑变化,像一条直线从起点滑到终点。steps(n) 把整个动画时长切成 n 段,每段结束时瞬间跳到下一个值,中间完全静止。打字机效果要的就是文字一格一格地出现,不能有滑动感,所以只有 steps() 能做到。
steps(n)把动画分成 n 步,每步瞬间跳变- 线性缓动是平滑过渡,看不到一格一格的跳动
steps()可配合宽度实现打字效果step-start与step-end跳点不同
打字机效果的完整实现
打字机的核心思路是让一个容器的宽度从 0 逐步增长到文字的总宽度,每增长一个字的宽度就露出一个字。用 overflow: hidden 把还没露出的部分藏起来,用 steps() 让宽度变化是跳变的而不是平滑的。
.typewriter {
/* ch 单位 = 当前字体里 "0" 的宽度,等宽字体下 1ch = 1 个字符 */
width: 0;
overflow: hidden;
white-space: nowrap;
border-right: 2px solid; /* 模拟光标 */
font-family: monospace;
/* 11 步对应 11 个字符,每步露出一个字 */
animation: typing 2s steps(11) forwards,
blink 0.7s step-end infinite;
}
@keyframes typing {
to { width: 11ch; }
}
@keyframes blink {
50% { border-color: transparent; }
}注意必须用等宽字体,否则 ch 单位不准,每步露出的字可能多一点或少一点。步数必须和字符数一致,多了会在最后多停一下,少了最后几个字会一起蹦出来。
jump-start 和 jump-end 的区别
steps() 的第二个参数控制每一步在哪个时刻跳变。默认是 jump-end,意思是每一步先停在旧值,快到下一步时才跳到新值。jump-start 相反,一进入这一步就立刻跳到新值。
对打字机效果来说,用 jump-end 更自然:动画开始时宽度还是 0,过了第一步的时长后才露出第一个字。如果用 jump-start,动画一开始就直接露出第一个字,最后一步结束时已经全部显示完了。
两种效果在时间线上差了「半步」,选哪个取决于你希望第一个字是立刻出现还是等一下再出现。
容易答错的地方
- 以为
steps()可以让任何属性看起来像跳变 steps()确实对所有可动画属性都生效,但效果好不好取决于属性本身。width从0到11ch分成 11 步,每步正好多露一个字,效果很好。但color从红到蓝分成 5 步,人眼看到的是 5 个不同的色块,可能不是你想要的。- 混淆
steps()的步数和关键帧数 - 关键帧的数量和
steps()的步数是两回事。steps(11)是在两个关键帧之间插入 11 个跳变点。如果@keyframes里写了 3 个关键帧(0%、50%、100%),每段之间各有 11 步,总共是 22 步。
面试官还会怎么问?
打字机效果能处理中文吗?
能,但中文不是等宽字体,ch 单位不准。一个中文字通常占两个 ch 的宽度。变通办法是用 em 单位手动算,或者用 JavaScript 获取实际文字宽度后再设 steps() 的步数。
steps() 还能做什么效果?
精灵图动画(sprite animation)是最典型的用法。把一组帧画在一张长图上,用 background-position 配合 steps() 一帧帧地切换。游戏里的角色走路动画、表情包动图都可以这样做。
线性缓动函数只有 linear 一种吗?
CSS 里 linear 是匀速的。ease、ease-in、ease-out 是贝塞尔曲线,也是连续平滑的但有加减速。cubic-bezier() 可以自定义曲线。这些都和 steps() 的离散跳变是两个体系,一个平滑一个跳变。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。