打字动画
有些时候,我们希望一段文本的字符逐个呈现,模拟出一种打字的效果。
核心思路就是让容器的宽度成为动画的主体:把所有文本包裹在这个容器中,然后让它的宽度从0开始以步进动画的方式、一个字一个字地扩张到它应有的宽度。它并不适用于多行文本。并且动画持续的事件越长,动画效果越差。
我们开始写代码:
h1.m-50 CSS is awesome
@keyframes typing {
from {width: 0;}
}
h1 {
font-family: monospace; /*等宽字体*/
width: 7.7em;
animation: typing 8s;
}
上面这段看似没什么问题的代码写出来却毫无正确的效果可言:
原来这是因为我们忘了用white-space:nowrap来阻止文本换行,因此文本的行数会随着宽度的扩张不断变化。 并且,我们忘了加上overflow:hidde;
@keyframes typing {
from {width: 0;}
}
h1 {
font-family: monospace;
width: 7.7em;
animation: typing 8s;
white-space: nowrap;
overflow: hidden;
}
这次尝试已经较为接近了,但还是有问题:
- 现在的字是平滑出现的,而不是一字一字地跳出来的。
- 长度是怎么算出来的?
我们 可以通过steps()来解决第一个问题,就像逐帧动画一样,但是,我们的step数量需要根据字符数来决定,这会显得难以维护,以后我们可以使用JavaScript写一个小脚本来解决这个问题。
第二个问题可以通过ch单位来缓解。这个ch单位是由CSS值与单位(第三版)规范引入的一个新蛋为,表示“0”字形的宽度,我们平时都不会关注0这个字符有多宽,但在等宽字体中,“0”字形的宽度和其他所有字形的宽度是一致的。
@keyframes typing {
from {width: 0;}
}
h1 {
font-family: monospace;
width: 15ch;
animation: typing 6s steps(15);
white-space: nowrap;
overflow:hidden;
}
上面这段代码和我们想要的效果已经非常接近了,只差一个闪烁的光标。
我们可以用一个伪元素来生成光标,并通过opacity属性来实现闪烁效果,我们也可以用右边框来模拟光标效果,这样就可以节省宝贵的伪元素资源作他用:
@keyframes typing {
from {width: 0;}
}
@keyframes caret {
50% {border-color: transparent;}
}
h1 {
font-family: monospace;
width: 15ch;
white-space: nowrap;
overflow: hidden;
border-right: 1px solid;
animation: typing 6s steps(15),
caret 1s infinite;
}
这样就实现了完整的打字动画效果了。
前面提到的,由于steps()中的字符数难以维护,因此我们通过下面这段JavaScript代码来算出字符数。
let h1 = document.querySelector('h1'),
len = h1.textContent.length,
style = h1.style;
style.width = len + 'ch';
style.animationTimingFunction = 'steps(' + len + ')';
在旧版本的浏览器中记得设置好回退机制。