CSS 揭秘(8)--打字动画

Posted by liveipool on February 9, 2017

打字动画

有些时候,我们希望一段文本的字符逐个呈现,模拟出一种打字的效果。

核心思路就是让容器的宽度成为动画的主体:把所有文本包裹在这个容器中,然后让它的宽度从0开始以步进动画的方式、一个字一个字地扩张到它应有的宽度。它并不适用于多行文本。并且动画持续的事件越长,动画效果越差。

我们开始写代码:

h1.m-50 CSS is awesome


@keyframes typing {
	from {width: 0;}
}

h1 {
	font-family: monospace;  /*等宽字体*/
	width: 7.7em;
	animation: typing 8s;
}

上面这段看似没什么问题的代码写出来却毫无正确的效果可言:
45.1.png

原来这是因为我们忘了用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;
}

45.2.png
这次尝试已经较为接近了,但还是有问题:

  1. 现在的字是平滑出现的,而不是一字一字地跳出来的。
  2. 长度是怎么算出来的?

我们 可以通过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 + ')';

在旧版本的浏览器中记得设置好回退机制。