打字机效果文字插件typer.js
typer.js介绍
typer.js是模拟打字机效果的轻型js插件(1.6 Kb),有打字、删除、重复和停顿几种简单的效果。
起因
由于这几天写个在线简历,我想使用打字的特效,在github上搜到一个叫TheaterJS的插件,效果很好,但是应用时发现,TheaterJS开发版的体积近80kb,而且我只需要它的打字机效果,不需其他复杂的功能,于是又萌生了自己造轮子的想法。
构思
之前写的listen2me用过类似的文字单播的效果,加上见过许多其他打字机效果,心里觉得还是有底的。
想了想自己的需求,效果上分为两块:
- 文字逐个显示
- 闪烁的光标
在使用上需要:
- 输出内容控制简便,模仿TheaterJs输出时的链式调用方法。
- 控制速度、停顿等
研究过程
一些错误的想法
打字效果很简单,简单的JS代码配合CSS就很好的还原了TheaterJS的效果,当然这仅是一行文字的输出。
然后研究输出控制。刚开始单纯的以为借用JQUERY可以直接链式调用与事件的顺序执行,具体写代码才发现,jquery的链式调用是操作节点的,而且它的内置函数应该都设定好了对链式操作的支持,我在网上查阅了一些资料,js可以通过对函数return this来支持链式调用。
又研究了事件的顺序,同样,本以为jquery的链式调用可以按顺序执行,比如$('DOM').fadeIn().fadeOut().animate(....)
这个操作就是按照淡入、淡出再动画的顺序进行的,后面的操作会等待前面的完成。写代码时又发现,我写的js无法以这个思路实现按序执行,因为打字效果要使用setTimeout
这个函数,使得整个过程成为异步,JS执行时自然无法有序。看了一些JQUERY代码,发现除了链式调用外,可以按序执行代码的方法还有使用callback
,比如``$('DOM').animate({'width':'100%'},1000,function(){........})
。
然后自己陷入了混乱,再乱试了各种方法之后,选择了睡觉。
今天早起,突然就想清楚昨天尝试的方法问题在哪里,比如这段事件等待:
while (!isTyping) {
this.isTyping = true;
speed = speed ? speed : this.speed;
var duration = text.length * speed;
var set = function() {
isTyping = true;
}
for (var i = 0; i < text.length; i++) {
setTimeout(addText, speed * i, text.charAt(i));
setTimeout(function() {
isTyping = false;
}, duration);
set()
}
}
用while其实是个死循环。
我的本意是建立一个isTyping标志,当第一个打字事件开始时,isTyping为true,这样第二个事件会因为while的条件不成立,一直在空转等待前一个事件完成时isTyping为false。但是,第一个事件完成时,isTyping为false的瞬间,仍是在while循环体内,又触发了while (!isTyping),所以永远都轮不到第二个事件,永远都停在了第一个事件内。
正确的思路
事件的顺序
继而决定使用想到的第二种思路,将所有的打字事件,计算出所消耗的时间,使用setTimeout
将每个事件用时累加起来,作为下一个事件的开始事件,也就是说:
a事件: typer.type('this is the first paragraph')
b事件: typer.type('this is the second paragraph')
.....
n事件: typer.type('this is the first paragraph')
a事件需要比如10s,那么b事件的开始时间就是10s后,也就是setTimeout(b事件,10000)
,依次类推,这样所有的打字事件都可以由setTimeout
控制其顺序了。
链式调用
再给每个type
函数加上return this,这样就可以链式调用了:
var babyTyper = new typer(DOM);
babyTyper
.type('I am a babyTyper')
.type('and I love typing in this way')
.type('I feel so happy!')
或者这样
babyTyper.type('I am brother of that guy,').type(' I love typing in a row, ').type('because it is cool!')
闪烁的打字光标效果
刚开始研究效果时,使用过INPUT
输入框,去除边框border
和outlook
后,只需获得输入焦点,JS将文字输入到input即可,同样是打字的效果,但是有一点不足,无法控制闪烁光标的样式,因为这个光标是继承自系统的,无论怎么调节CSS字体样式,光标都是那么细小,跳动的频率也控制不了。
这里就借鉴了TheaterJS的方法,使用::after
伪元素给内容加入一个动画内容:
.typer-place::after {
content: '|';
animation: blink 800ms infinite;
}
@keyframes blink {
from { opacity: 0; }
to { opacity: 1; }
}
这样就实现了打字时闪烁光标的效果。
循环重复
功能都完成后,我又想到需要一个循环播放的功能。想了一会,想到一个最基础的方法,就是建立一个记录操作的方法,把每次的输出与删除都按顺序记录。然后再把记录的内容再次执行就可以。看了几个源码,他们写的重复也是记录操作再次执行。
但是我总觉得这样太麻烦,不够简便,不如想一种方法无需记录,直接重复。查阅了些资料,好像没有什么现成的例子重复执行代码的,最终写成如下:
var typer = new typer(DOM);
setInterval(repeat,typer.duration)
function repeat(){
typer.type('测试')
typer.type('测试')
typer.type('测试')
}
这个逻辑很清晰,我很喜欢,但是写出来的内容太多了,不够简洁。最终我还是选择了记录操作,实现重复只需一个方法typer.repeat()
。
以上几点是代码的关键部分,完成之后,其余的内容都相对简单,很快就处理完了。
源码及使用
源码在我的github
使用方法很简单
<script src="typer.js"></script>
<script>
var typer = new typer('DOM的ID'))
typer.type('hi~')
typer.type('当然也可以按照链式调用那样省略typer')
</script>
具体使用方法参见github readme
小结
总体来讲,就是打字事件按顺序调用和寻找简便重复方法这两部分想的挺久的,其余都很简单。