该案例学习自B站UP主xiao-high
打印多条文段可以参考一下这里:传送门。案例见博客个人简介的应用,点这里看效果。(打印一段文字后,再打印一段,覆盖前边的内容)
介绍
文本一个一个的显示,同时有光标闪烁效果。
点击这里查看效果:传送门
动手制作
新建一个HTML文件。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>qsdbl</title>
</head>
<body>
</body>
</html>
body
添加一个div,class名为text,用于控制文本和光标的大小。div内部添加两个span,分别作为文本和光标的容器。光标为符号|
。
<div class="text">
<span class="word"></span><span class="gbiao">|</span>
</div>
css
去除浏览器默认的margin、padding值。
* {
margin: 0;
padding: 0;
}
body设置让div居中显示,设置一个径向渐变背景。
body {
height: 100vh;
/*让文本居中*/
display: flex;
align-items: center;
justify-content: center;
/*径向渐变*/
background: radial-gradient(#000000, rgb(31, 77, 20));
}
设置div内的字体颜色为白色,字体大小在后边js代码中设置。
.text {
color: #fff;
text-align:center;
}
实现光标闪烁
制作光标闪烁动画:动画设置两帧,透明度从0到100。
@keyframes flash {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
光标应用该动画,动画播放时间为0.5秒,线性播放,循环播放。同时设置左外边距为5px。
.text .gbiao {
margin-left: 5px;
animation: flash 0.5s linear infinite;
/*让光标闪烁*/
}
script
参数说明:
fontSize
,字体大小word
,显示文本的span标签str
,文本
在进行绘制之前,我们需要确定打开HTML文件的设备是pc还是手机。从而指定不同的fontSize。
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";
上边的js代码可以使用CSS中的@media 查询
来实现,具体代码如下:
在前边css的.text
中添加字体大小:(PC端)
font-size: 28px;
在前边的css后边再添加一个style
标签,指定移动端的字体大小。代码如下:
<style type="text/css">
@media(max-width: 500px) {
.text{
font-size: 50px;
}
}
</style>
判断设备类型的js代码和设置文本大小的js代码就可以删掉了。
添加要显示的文本
let str = "你好,我是轻率的保罗!很高兴见到你!"; //要显示的文本
通过class名选择显示文本的span标签
//获取文本显示的span
let word = document.querySelector('.word');
实现打字机效果
有了str和word我们就可以实现打字机效果了。将实现打字机效果封装在一个方法内,方法名showText
//实现打字机效果
function showText() {
}
我们定义几个变量:
myflag
,布尔值,用于后边的正倒放中- time,每个字符显示的时间长度
timewait
,正倒放等待时间
let myflag = true;
let time = 300; //每个字符显示的时间
let timewait = 2000; //正倒放等待时间
实现正放
我们规定,正放就是从没有显示字符到显示出全部的文本的过程。反之为倒放。
正放思路:使用一个for循环,循环次数为要显示的文本的个数,每进入一个循环就截取要显示的文本的一部分显示在页面上。例如第一次进入for循环,截取“你”;第二次进入for循环,截取“你好”;第三次进入for循环,截取“你好,”;第四次进入for循环,截取“你好,我”;。。。代码实现如下:
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,。。。所以越往后时间越长。代码实现如下:
setTimeout(function (){
word.innerHTML = str.substr(0, n);
},(n - 1) * time);//单位是毫秒
所以实现正放代码应该为:
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
不变,所以越往后时间越长,跟前边正放一样。代码实现如下:
for (let j = str.length; j >= 0; j--) { //倒放
setTimeout(function() { //定时器
word.innerHTML = str.substr(0, j);
}, (str.length - j) * time);
}
正/倒放结合
实现的效果是先正放,然后倒放,再然后是正放一直循环下去。这里我们需要用到一个周期定时器,每隔一定的时间(正放或倒放结束后),就执行一次倒放或正放。
setInterval(function (){
},毫秒数);
每隔一定的时间,这个时间我们可以通过str.length * time
来计算,str.length * time
为一次正放或倒放所用的时间,如果想让正放倒放相隔一定的时间,我可以再添加一个等待时间,即前边定义的timewait。
setInterval(function (){
}, str.length * time + timewait);
要实现一次正放一次倒放交替执行,我们需要用到在前边定义的布尔类型变量myflag,配合if-else语句即可实现。代码如下:
if(myflag){
//正放
}else{
//倒放
}
myflag = !myflag;
所以正/倒放结合完整代码如下:
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的完整代码如下:
//实现打字机效果
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后的完整代码如下:
//实现打字机效果
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再调用实现打字机效果的函数。代码如下:
//打开页面1s后显示文本
setTimeout(showText, 1000);
源码
至此全部的代码都完成了。下边是全部代码整合:
<!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)
-
各形参分别为:显示文本的容器,显示的文本,一个字符显示时间,正倒放间隔时间
-
正、倒放函数都是在正、倒放操作的基础上加一个定时器,添加等待时间
//倒放 setTimeout(function() { //内部的实现代码与前边的一样,并未修改 for (let j = str.length; j >= 0; j--) { //倒放 setTimeout(function() { //定时器 element.innerHTML = str.substr(0, j); }, (str.length - j) * time); } }, timewait);//使用定时器增加等待时间
-
正放函数的后边调用倒放函数,计算好正放时间加等待时间后使用定时器调用
//正放 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次),使用时如果不想频繁设置这些参数,只需要给显示文本的容器和要显示的文本两个参数即可。
使用:
//获取文本显示的span(显示文本的容器)
let word = document.querySelector('.word');
showText02(word,str,4);//显示文本的容器、显示的文本,播放次数(正+倒为一次),一个字符显示时间,正倒放间隔时间
实现打字机效果的一些函数:
//实现方法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);//使用定时器增加等待时间
}
完整源代码如下:
<!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>
原文链接:实现打字机特效