0%

打字机效果

|


该案例学习自B站UP主xiao-high

打印多条文段可以参考一下这里:传送门。案例见博客个人简介的应用,点这里看效果。(打印一段文字后,再打印一段,覆盖前边的内容)

介绍

文本一个一个的显示,同时有光标闪烁效果。

点击这里查看效果:传送门

动手制作

新建一个HTML文件。

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>qsdbl</title>
</head>
<body>

</body>
</html>

body

添加一个div,class名为text,用于控制文本和光标的大小。div内部添加两个span,分别作为文本和光标的容器。光标为符号|

1
2
3
<div class="text">
<span class="word"></span><span class="gbiao">|</span>
</div>

css

去除浏览器默认的margin、padding值。

1
2
3
4
* {
margin: 0;
padding: 0;
}

body设置让div居中显示,设置一个径向渐变背景。

1
2
3
4
5
6
7
8
9
body {
height: 100vh;
/*让文本居中*/
display: flex;
align-items: center;
justify-content: center;
/*径向渐变*/
background: radial-gradient(#000000, rgb(31, 77, 20));
}

设置div内的字体颜色为白色,字体大小在后边js代码中设置。

1
2
3
4
.text {
color: #fff;
text-align:center;
}

实现光标闪烁

制作光标闪烁动画:动画设置两帧,透明度从0到100。

1
2
3
4
5
6
7
8
9
@keyframes flash {
from {
opacity: 0;
}

to {
opacity: 1;
}
}

光标应用该动画,动画播放时间为0.5秒,线性播放,循环播放。同时设置左外边距为5px。

1
2
3
4
5
.text .gbiao {
margin-left: 5px;
animation: flash 0.5s linear infinite;
/*让光标闪烁*/
}

script

参数说明:

  • fontSize,字体大小
  • word,显示文本的span标签
  • str,文本

在进行绘制之前,我们需要确定打开HTML文件的设备是pc还是手机。从而指定不同的fontSize。

1
2
3
4
5
6
var ua = navigator.userAgent.toLowerCase();
if(/AppleWebKit.*Mobile/i.test(navigator.userAgent) || (/MIDP|SymbianOS|NOKIA|SAMSUNG|LG|NEC|TCL|Alcatel|BIRD|DBTEL|Dopod|PHILIPS|HAIER|LENOVO|MOT-|Nokia|SonyEricsson|SIE-|Amoi|Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|ZTE/.test(navigator.userAgent)) || ua.match(/MicroMessenger/i) == "micromessenger"){
var fontSize = 50;//手机字体大小
}else{
var fontSize = 28;//pc字体大小
}

设置文本大小

1
2
3
//设置文本大小
let textSize = document.querySelector('.text');
textSize.style.fontSize = fontSize + "px";

上边的js代码可以使用CSS中的@media 查询来实现,具体代码如下:

在前边css的.text中添加字体大小:(PC端)

1
font-size: 28px;

在前边的css后边再添加一个style标签,指定移动端的字体大小。代码如下:

1
2
3
4
5
6
7
<style type="text/css">
@media(max-width: 500px) {
.text{
font-size: 50px;
}
}
</style>

判断设备类型的js代码和设置文本大小的js代码就可以删掉了。

添加要显示的文本

1
let str = "你好,我是码代码的冰果果!很高兴见到你!"; //要显示的文本

通过class名选择显示文本的span标签

1
2
//获取文本显示的span
let word = document.querySelector('.word');

实现打字机效果

有了str和word我们就可以实现打字机效果了。将实现打字机效果封装在一个方法内,方法名showText

1
2
3
4
//实现打字机效果
function showText() {

}

我们定义几个变量:

  • myflag,布尔值,用于后边的正倒放中
  • time,每个字符显示的时间长度
  • timewait,正倒放等待时间
1
2
3
let myflag = true;
let time = 300; //每个字符显示的时间
let timewait = 2000; //正倒放等待时间

实现正放

我们规定,正放就是从没有显示字符到显示出全部的文本的过程。反之为倒放。

正放思路:使用一个for循环,循环次数为要显示的文本的个数,每进入一个循环就截取要显示的文本的一部分显示在页面上。例如第一次进入for循环,截取“你”;第二次进入for循环,截取“你好”;第三次进入for循环,截取“你好,”;第四次进入for循环,截取“你好,我”;。。。代码实现如下:

1
2
3
4
for (let n = 1; n <= str.length; n++) { //正放
word.innerHTML = str.substr(0, n);
}
//substr(start,length)方法可在字符串中抽取从 start 下标开始的指定数目的字符。

可是电脑运行速度都是很快的,一瞬间for循环就执行完了,也就是说我们看不到文本一个一个的显示出来。我们需要控制每一次截取文本显示到页面的时间。

我们可以使用一个定时器,隔指定时间执行定义在其内部的函数。前边我们定义了每个字符显示的时间为time=300毫秒,前一个字符等待显示的时间为0,前两个字符等待显示的时间为time,前三个字符等待显示的时间为两个time,前四个字符等待显示的时间为三个time,。。。所以越往后时间越长。代码实现如下:

1
2
3
setTimeout(function (){
word.innerHTML = str.substr(0, n);
},(n - 1) * time);//单位是毫秒

所以实现正放代码应该为:

1
2
3
4
5
for (let n = 1; n <= str.length; n++) { //正放
setTimeout(function() { //定时器
word.innerHTML = str.substr(0, n);
}, (n - 1) * time);
}

实现倒放

倒放与正放刚刚好相反。正放是先显示前一个字符,再显示前两个字符,。。。倒放是先显示全部的字符,然后少显示一个,再然后少显示两个,。。。所以j的初始值为字符的长度str.length。时间变成了str.length - j ,j越来越小,str.length不变,所以越往后时间越长,跟前边正放一样。代码实现如下:

1
2
3
4
5
for (let j = str.length; j >= 0; j--) { //倒放
setTimeout(function() { //定时器
word.innerHTML = str.substr(0, j);
}, (str.length - j) * time);
}

正/倒放结合

实现的效果是先正放,然后倒放,再然后是正放一直循环下去。这里我们需要用到一个周期定时器,每隔一定的时间(正放或倒放结束后),就执行一次倒放或正放。

1
2
3
setInterval(function (){

},毫秒数);

每隔一定的时间,这个时间我们可以通过str.length * time来计算,str.length * time为一次正放或倒放所用的时间,如果想让正放倒放相隔一定的时间,我可以再添加一个等待时间,即前边定义的timewait

1
2
3
setInterval(function (){

}, str.length * time + timewait);

要实现一次正放一次倒放交替执行,我们需要用到在前边定义的布尔类型变量myflag,配合if-else语句即可实现。代码如下:

1
2
3
4
5
6
if(myflag){
//正放
}else{
//倒放
}
myflag = !myflag;

所以正/倒放结合完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
setInterval(function(myflag) {
if (this.myflag) {
for (let i = 1; i <= str.length; i++) { //正放
setTimeout(function() { //定时器
word.innerHTML = str.substr(0, i);
}, (i - 1) * time);
}
} else {
for (let j = str.length; j >= 0; j--) { //倒放
setTimeout(function() { //定时器
word.innerHTML = str.substr(0, j);
}, (str.length - j) * time);
}
}
this.myflag = !this.myflag;
}, str.length * time + timewait);

到这里,实现打字机效果的函数showText,貌似就已经完成了。不过我在实际的测试中发现有一个bug,与传递到周期定时器内的参数myflag有关。导致第一次正放没有显示,到了倒放才开始显示文本。这个bug目前我修复不了,不过我在周期定时器的前边添加了一个正放,可以掩盖掉那个bug。如果你解决了这个bug请一定要跟我分享一下解决方法。函数showText未掩盖bug的完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//实现打字机效果
function showText() {
let myflag = true;
let time = 300; //每个字符显示的时间
let timewait = 2000; //正倒放等待时间
setInterval(function(myflag) {
if (this.myflag) {
for (let i = 1; i <= str.length; i++) { //正放
setTimeout(function() { //定时器
word.innerHTML = str.substr(0, i);
}, (i - 1) * time);
}
} else {
for (let j = str.length; j >= 0; j--) { //倒放
setTimeout(function() { //定时器
word.innerHTML = str.substr(0, j);
}, (str.length - j) * time);
}
}
this.myflag = !this.myflag;
}, str.length * time + timewait);
}

函数showText掩盖bug后的完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//实现打字机效果
function showText() {
let myflag = true;
let time = 300; //每个字符显示的时间
let timewait = 2000; //正倒放等待时间
for (let n = 1; n <= str.length; n++) { //正放
setTimeout(function() { //定时器
word.innerHTML = str.substr(0, n);
}, (n - 1) * time);
}
setInterval(function(myflag) {
if (this.myflag) {
for (let i = 1; i <= str.length; i++) { //正放
setTimeout(function() { //定时器
word.innerHTML = str.substr(0, i);
}, (i - 1) * time);
}
} else {
for (let j = str.length; j >= 0; j--) { //倒放
setTimeout(function() { //定时器
word.innerHTML = str.substr(0, j);
}, (str.length - j) * time);
}
}
this.myflag = !this.myflag;
}, str.length * time + timewait);
}

最后,我们需要调用一下这个函数,才能使打字机效果生效。我这里是添加了一个定时器,页面加载完成之后过1s再调用实现打字机效果的函数。代码如下:

1
2
//打开页面1s后显示文本
setTimeout(showText, 1000);

源码

至此全部的代码都完成了。下边是全部代码整合:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>qsdbl</title>
</head>
<body>
<div class="text">
<span class="word"></span><span class="gbiao">|</span>
</div>
</body>
<style>
* {
margin: 0;
padding: 0;
}

body {
height: 100vh;
/*让文本居中*/
display: flex;
align-items: center;
justify-content: center;
/*径向渐变*/
background: radial-gradient(#000000, rgb(31, 77, 20));
}

.text {
color: #fff;
}

.text .gbiao {
margin-left: 5px;
animation: flash 0.5s linear infinite;
/*让光标闪烁*/
font-weight: bold;
}

@keyframes flash {
from {
opacity: 0;
}

to {
opacity: 1;
}
}
</style>
<script>
var ua = navigator.userAgent.toLowerCase();
if (/AppleWebKit.*Mobile/i.test(navigator.userAgent) || (
/MIDP|SymbianOS|NOKIA|SAMSUNG|LG|NEC|TCL|Alcatel|BIRD|DBTEL|Dopod|PHILIPS|HAIER|LENOVO|MOT-|Nokia|SonyEricsson|SIE-|Amoi|Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|ZTE/
.test(navigator.userAgent)) || ua.match(/MicroMessenger/i) == "micromessenger") {
var fontSize = 50; //手机字体大小
} else {
var fontSize = 28; //pc字体大小
}
//设置文本大小
let textSize = document.querySelector('.text');
textSize.style.fontSize = fontSize + "px";

let str = "你好,我是码代码的冰果果!很高兴见到你!"; //要显示的文本

//获取文本显示的span
let word = document.querySelector('.word');
//打开页面1s后显示文本
setTimeout(showText, 1000);

//实现打字机效果
function showText() {
let myflag = true;
let time = 300; //每个字符显示的时间
let timewait = 2000; //正倒放等待时间
for (let n = 1; n <= str.length; n++) { //正放
setTimeout(function() { //定时器
word.innerHTML = str.substr(0, n);
}, (n - 1) * time);
}
setInterval(function(myflag) {
if (this.myflag) {
for (let i = 1; i <= str.length; i++) { //正放
setTimeout(function() { //定时器
word.innerHTML = str.substr(0, i);
}, (i - 1) * time);
}
} else {
for (let j = str.length; j >= 0; j--) { //倒放
setTimeout(function() { //定时器
word.innerHTML = str.substr(0, j);
}, (str.length - j) * time);
}
}
this.myflag = !this.myflag;
}, str.length * time + timewait);
}
</script>
</html>

修复bug

使用闭包来解决bug,详细笔记访问这里。(这里的也是使用了闭包,不过定时器内并没有使用到外边定义的标志位myflag,函数定义的问题。定时器内的this.myflag不是外边定义的myflag,而是匿名函数传递进来的空值重新被后边的this.myflag = !this.myflag;语句赋值了的定时器内的局部变量,所以第一次没有执行if中的正放代码是因为其值为null。解决方法是把定时器内的匿名函数拿出来,定时器内调用函数名即可。)

打字机效果案例源码:https://cloud.189.cn/t/MbYraiArERrq(访问码:hzj6)

修复bug

下边的方法已经废弃,有了更好的实现方法。已放到上边。这里的方法也可以实现效果,但是过于凌乱。

其实bug并没有修复,而是使用了另一种方法来实现打字机效果。将正、倒放单独作为一个函数,不过在正放的函数结束后调用倒放的函数。再使用一个函数调用正放的函数。

  • 正、倒放函数,positive(element,str, time, timewait)、reverse(element,str, time, timewait)

    • 各形参分别为:显示文本的容器,显示的文本,一个字符显示时间,正倒放间隔时间

    • 正、倒放函数都是在正、倒放操作的基础上加一个定时器,添加等待时间

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      //倒放
      setTimeout(function() {


      //内部的实现代码与前边的一样,并未修改
      for (let j = str.length; j >= 0; j--) { //倒放
      setTimeout(function() { //定时器
      element.innerHTML = str.substr(0, j);
      }, (str.length - j) * time);
      }


      }, timewait);//使用定时器增加等待时间
    • 正放函数的后边调用倒放函数,计算好正放时间加等待时间后使用定时器调用

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      //正放
      setTimeout(function() {
      for (let i = 1; i <= str.length; i++) { //正放
      setTimeout(function() { //定时器
      element.innerHTML = str.substr(0, i);
      }, (i - 1) * time);
      }


      //调用倒放
      setTimeout( 调用倒放 , 正放所用时间+等待时间 );



      }, timewait);//播放前的等待时间
  • 调用正放的函数,showText02(element, str, num,time,timewait),形参有:显示文本的容器,显示的文本,播放次数(正+倒为一次),一个字符显示时间,正倒放间隔时间

    • 使用for循环,实现指定的播放次数。不过在调用正放函数时需要使用定时器。
    • 可以将正放的函数看作是一次正放加倒放(正放后边调用了倒放),所以定时器的时间可以设置为一次正放的时间+一次倒放的时间+两个等待时间,即:str.length * time * 2 + timewait*2
    • showText02有四个参数,但是我们可以内置字符显示时间(200ms)、等待时间(2000ms)和播放次数 (1次),使用时如果不想频繁设置这些参数,只需要给显示文本的容器要显示的文本两个参数即可。

使用:

1
2
3
//获取文本显示的span(显示文本的容器)
let word = document.querySelector('.word');
showText02(word,str,4);//显示文本的容器、显示的文本,播放次数(正+倒为一次),一个字符显示时间,正倒放间隔时间

实现打字机效果的一些函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//实现方法2
function showText02(element,str, num,time,timewait) {
//time,每个字符显示的时间,建议200毫秒
//timewait,正倒放等待时间,建议2000毫秒
//num,播放次数,默认一次
if(time == null){
time = 200;
}
if(timewait == null){
timewait = 2000;
}
if(num == null){
num = 1;
}
for (var i = 0; i < num; i++) {//for循环控制播放次数
setTimeout(function() {
positive(element,str, time, timewait);
}, (str.length * time * 2 + timewait*2) * i);//括号内的时间为一次正加一次倒
}
}
//正放
function positive(element,str, time, timewait) {
setTimeout(function() {
for (let i = 1; i <= str.length; i++) { //正放
setTimeout(function() { //定时器
element.innerHTML = str.substr(0, i);
}, (i - 1) * time);
}
setTimeout(function() {
reverse(element,str, time);
}, str.length * time + timewait);//正放结束后再开始倒放,使用定时器增加正放时间和结束后的等待时间
}, timewait);//播放前的等待时间
}
//倒放
function reverse(element,str, time, timewait) {
setTimeout(function() {
for (let j = str.length; j >= 0; j--) { //倒放
setTimeout(function() { //定时器
element.innerHTML = str.substr(0, j);
}, (str.length - j) * time);
}
}, timewait);//使用定时器增加等待时间
}

完整源代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>qsdbl</title>
</head>
<body>
<div class="text">
<span class="word"></span><span class="gbiao">|</span>
</div>
</body>
<style type="text/css">
* {
margin: 0;
padding: 0;
}

body {
height: 100vh;
/*让文本居中*/
display: flex;
align-items: center;
justify-content: center;
/*径向渐变*/
background: radial-gradient(#000000, rgb(64, 204, 15));
}

.text {
color: #fff;
/*0f0*/
text-align: center;
font-size: 40px;
font-weight: bold;
}

.text .gbiao {
margin-left: 5px;
animation: flash 0.5s linear infinite;
/*让光标闪烁*/
}

@keyframes flash {
from {
opacity: 0;
}

to {
opacity: 1;
}
}
</style>
<style type="text/css">
/*当显示设备为手机时,字体大小设置为20px。(用于判断设备类型的500px是一个经验值)*/
@media(max-width:500px) {
.text {
font-size: 20px;
}
}
</style>
<script>
let str = "你好,我是码代码的冰果果!很高兴见到你!"; //要显示的文本
//获取文本显示的span(显示文本的容器)
let word = document.querySelector('.word');
//播放4次
showText02(word,str,4);//显示文本的容器,显示的文本,播放次数(正+倒为一次),一个字符显示时间,正倒放间隔时间


//实现打字机效果(下边为封装的函数)
function showText02(element,str, num,time,timewait) {
//time,每个字符显示的时间,建议200毫秒
//timewait,正倒放等待时间,建议2000毫秒
if(time == null){
time = 200;
}
if(timewait == null){
timewait = 2000;
}
if(num == null){
num = 1;
}
for (var i = 0; i < num; i++) {//for循环控制播放次数
setTimeout(function() {
positive(element,str, time, timewait);
}, (str.length * time * 2 + timewait*2) * i);//括号内的时间为一次正加一次倒
}
}
//正放
function positive(element,str, time, timewait) {
setTimeout(function() {
for (let i = 1; i <= str.length; i++) { //正放
setTimeout(function() { //定时器
element.innerHTML = str.substr(0, i);
}, (i - 1) * time);
}
setTimeout(function() {
reverse(element,str, time);
}, str.length * time + timewait);//正放结束后再开始倒放,使用定时器增加正放时间和结束后的等待时间
}, timewait);//播放前的等待时间
}
//倒放
function reverse(element,str, time, timewait) {
setTimeout(function() {
for (let j = str.length; j >= 0; j--) { //倒放
setTimeout(function() { //定时器
element.innerHTML = str.substr(0, j);
}, (str.length - j) * time);
}
}, timewait);//使用定时器增加等待时间
}
</script>
</html>
若图片不能正常显示,请在浏览器中打开

欢迎关注我的其它发布渠道