html手写一个打印机效果-从最基础到学会
手写一个打印机效果
啥叫打印机效果,话不多说,直接上效果。我们可以自己写入一段文本然后通过html的方式,让它跟打印机一样,一个一个的打印到页面,并且还可以一个一个的删除。在这里我先浅说一下,我们的实现技巧,定时器setTimeout
控制时间,然后for循环遍历
写入到页面上。
封装的打印jsmain(str,text)直接传入要写入的数组对象和要写入的元素。
copy.js 下载到本地引入然后调用它就可以了
代码
先拿到我们要写入的元素,然后设置好我们要写入的内容。
var text = document.querySelector('.text');
var str = ['你好 ,我是一名刚入坑不久的大三在校生。', '现在学习都是为了将来的工作。', '希望能够得到大家的鼓励,谢谢!']
基础代码一
首先这里,我们先实现一个只有一段文字的实现效果。实现思路就是通过计时器,控制好时间,每次写入的文字通过str[0].substr(0, k)
拿到,需要注意的是,因为是异步任务,回退的时候,我们的时间要设置好,加上写入完的时间1000 + 200 * str[0].length)
。
写入
for (let j = 0; j < str[0].length; j++) {
// 使用 setTimeout 函数来实现每个字符的延时输出
setTimeout(() => {
text.innerHTML = str[0].substr(0, j) // 显示当前字符串的前 j 个字符
}, 200 * j) // 延迟时间为 200 毫秒乘以 j,即每个字符间隔 200 毫秒
}
// 回退
// 在所有字符输出完成后,等待 1000 毫秒后开始回退
setTimeout(() => {
for (let k = str[0].length, i = 0; k >= 0; k--, i++) {
// 使用 setTimeout 函数来实现每个字符的延时输出
setTimeout(() => {
text.innerHTML = str[0].substr(0, k) // 显示当前字符串的前 k 个字符
}, 200 * i) // 延迟时间为 200 毫秒乘以 i,即每个字符间隔 200 毫秒
}
}, 1000 + 200 * str[0].length) // 等待时间为 1000 毫秒加上所有字符输出的延时时间
基础代码二 错误代码
首先这个代码是错误的
为了能让大家更好的看到错误的效果,于是我把这个代码也上传了。大家可以看到,在这里,页面上的文字总是会莫名奇怪的出现删除,根本不是我们想要的。其实我们也只是对上面一个代码进行了一个for循环遍历,却出现了这样的效果。其实这导致的原因就是setTimeout是异步任务
,时间没有控制好。即每个字符串的打印和删除都是异步任务,无法保证它们的执行顺序。因此,可能会出现多个字符串的打印和删除任务交错执行的情况,导致效果不符合预期。
// 即每个字符串的打印和删除都是异步任务,无法保证它们的执行顺序。因此,可能会出现多个字符串的打印和删除任务交错执行的情况,导致效果不符合预期。
// 整个str 这是一个有问题的代码 因为计算时间太麻烦了 都是异步任务
// for (let s = 0; s < str.length; s++) {
// // 写入
// for (let j = 0; j < str[s].length; j++) {
// setTimeout(() => {
// text.innerHTML = str[s].substr(0, j)
// }, 200 * j)
// }
// // 回退
// setTimeout(() => {
// for (let k = str[s].length, i = 0; k >= 0; k--, i++) {
// setTimeout(() => {
// text.innerHTML = str[s].substr(0, k)
// }, 200 * i)
// }
// }, 1000 + 200 * str[s].length)
// }
基础代码三
为了解决上面的问题,我们使用了函数封装并且使用了回调函数实现我们想要的效果。我们将打印和删除都封装成一个含有回调函数的函数,为什么要含有回调函数呢?这是为了我们下面对一个字符串打印和删除的函数做封装。打我们打印完一个字符串时,我们才会执行删除。所有我们将删除函数放到打印的回调函数中去执行。然后我们将打印整个字符串数组进行封装,因为我们在删除的里面也有一个回调函数,那么我们可以在这个回调函数里去执行打印下一条字符串,这样就防止了控制时间不准确的问题。
// 打印字符串
function printText(str, callback) {
var i = 0;
var timer = setInterval(function () {
text.innerHTML = str.substr(0, i); // 将字符串的前缀赋值给显示文本的元素
i++;
if (i > str.length) { // 如果已经打印完整个字符串
clearInterval(timer); // 停止定时器
callback && callback(); // 调用回调函数
}
}, 200); // 每 200 毫秒打印一个字符
}
// 删除字符串
function deleteText(str, callback) {
var i = str.length;
var timer = setInterval(function () {
text.innerHTML = str.substr(0, i); // 将字符串的前缀赋值给显示文本的元素
i--;
if (i < 0) { // 如果已经删除到空字符串
clearInterval(timer); // 停止定时器
callback && callback(); // 调用回调函数
}
}, 200); // 每 200 毫秒删除一个字符
}
// 打印和删除字符串
function printAndDeleteText(str, callback) {
printText(str, function () { // 先打印字符串
setTimeout(function () {
deleteText(str, callback); // 等待 1 秒后再删除字符串
}, 1000);
});
}
// 循环遍历字符串数组,依次打印和删除字符串
function printAndDeleteAllText(strArr) {
function printAndDeleteNext(i) {
if (i >= strArr.length) { // 如果已经处理完所有字符串
printAndDeleteNext(0); // 重新从头开始处理
} else {
printAndDeleteText(strArr[i], function () { // 先打印字符串
i++;
printAndDeleteNext(i); // 递归调用自身,处理下一个字符串
});
}
}
printAndDeleteNext(0); // 开始处理第一个字符串
}
// 开始打印和删除字符串数组中的所有字符串
printAndDeleteAllText(str)
最优代码
其实我们做了,这么多,最后就是为了解决异步任务。
所以我这里直接采用Promise
和async await
解决上面的问题。我们通过Promise
解决实现打印和删除的异步任务。我们通过async await
封装整个运行函数,解决了定时器异步问题,不用再计算时间,又难有算不出来。
// 最终版 封装 解决异步任务
function writeText(t, delay = 200) {
return new Promise((resolve, reject) => {
setTimeout(() => {
text.innerHTML = t; // 显示当前字符串 t
resolve(); // Promise 完成
}, delay) // 延迟 delay 毫秒后执行
})
}
async function main(str) {
while (true) { // 无限循环
for (let j = 0; j < str.length; j++) {
// 写入
for (let i = 0; i <= str[j].length; i++) {
await writeText(str[j].substr(0, i)) // 显示当前字符串的前 i 个字符
}
// 回退
// 回退前先等一秒
await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(); // 等待 1000 毫秒后 Promise 完成
}, 1000) // 等待 1000 毫秒
})
for (let i = str[j].length; i >= 0; i--) {
await writeText(str[j].substr(0, i), 200) // 显示当前字符串的前 i 个字符,间隔 200 毫秒
}
}
}
}
main(str)
源码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>打印机效果</title>
<style>
.container {
display: flex;
/* 使用 flex 布局 */
flex-direction: column;
/* 垂直布局 */
align-items: center;
/* 水平居中 */
justify-content: center;
/* 垂直居中 */
height: 100vh;
/* 高度占满整个视口 */
}
h1 {
font-size: 3rem;
/* 字体大小 */
margin-bottom: 2rem;
/* 底部间距 */
text-align: center;
/* 居中对齐 */
}
.text {
font-size: 2rem;
/* 字体大小 */
font-weight: bold;
/* 字体加粗 */
text-align: center;
/* 居中对齐 */
border-right: 2px solid black;
/* 添加光标效果 */
white-space: nowrap;
/* 不换行 */
overflow: hidden;
/* 隐藏超出部分 */
animation: blink 0.5s step-end infinite;
/* 添加光标闪烁效果 */
height: 3rem;
/* 设置一个固定的高度 */
}
@keyframes blink {
from,
to {
border-color: transparent;
/* 透明边框颜色 */
}
50% {
border-color: black;
/* 黑色边框颜色 */
}
}
</style>
</head>
<body>
<div class="container">
<h1>逐字打印和删除文字效果</h1>
<p class="text"></p>
</div>
</body>
<script>
var text = document.querySelector('.text');
var str = ['你好 ,我是一名刚入坑不久的大三在校生。', '现在学习都是为了将来的工作。', '希望能够得到大家的鼓励,谢谢!']
// 写入
// for (let j = 0; j < str[0].length; j++) {
// // 使用 setTimeout 函数来实现每个字符的延时输出
// setTimeout(() => {
// text.innerHTML = str[0].substr(0, j) // 显示当前字符串的前 j 个字符
// }, 200 * j) // 延迟时间为 200 毫秒乘以 j,即每个字符间隔 200 毫秒
// }
// // 回退
// // 在所有字符输出完成后,等待 1000 毫秒后开始回退
// setTimeout(() => {
// for (let k = str[0].length, i = 0; k >= 0; k--, i++) {
// // 使用 setTimeout 函数来实现每个字符的延时输出
// setTimeout(() => {
// text.innerHTML = str[0].substr(0, k) // 显示当前字符串的前 k 个字符
// }, 200 * i) // 延迟时间为 200 毫秒乘以 i,即每个字符间隔 200 毫秒
// }
// }, 1000 + 200 * str[0].length) // 等待时间为 1000 毫秒加上所有字符输出的延时时间
// 即每个字符串的打印和删除都是异步任务,无法保证它们的执行顺序。因此,可能会出现多个字符串的打印和删除任务交错执行的情况,导致效果不符合预期。
// 整个str 这是一个有问题的代码 因为计算时间太麻烦了 都是异步任务
// for (let s = 0; s < str.length; s++) {
// // 写入
// for (let j = 0; j < str[s].length; j++) {
// setTimeout(() => {
// text.innerHTML = str[s].substr(0, j)
// }, 200 * j)
// }
// // 回退
// setTimeout(() => {
// for (let k = str[s].length, i = 0; k >= 0; k--, i++) {
// setTimeout(() => {
// text.innerHTML = str[s].substr(0, k)
// }, 200 * i)
// }
// }, 1000 + 200 * str[s].length)
// }
// 最终版 封装 解决异步任务
function writeText(t, delay = 200) {
return new Promise((resolve, reject) => {
setTimeout(() => {
text.innerHTML = t; // 显示当前字符串 t
resolve(); // Promise 完成
}, delay) // 延迟 delay 毫秒后执行
})
}
async function main(str) {
while (true) { // 无限循环
for (let j = 0; j < str.length; j++) {
// 写入
for (let i = 0; i <= str[j].length; i++) {
await writeText(str[j].substr(0, i)) // 显示当前字符串的前 i 个字符
}
// 回退
// 回退前先等一秒
await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(); // 等待 1000 毫秒后 Promise 完成
}, 1000) // 等待 1000 毫秒
})
for (let i = str[j].length; i >= 0; i--) {
await writeText(str[j].substr(0, i), 200) // 显示当前字符串的前 i 个字符,间隔 200 毫秒
}
}
}
}
main(str)
</script>
</html>
来源:juejin.cn/post/7225178555827191868