记录一个工作时遇到的开发需求:
使用JavaScript实现的文本逐字显示效果,并且伴随着一个光标的动画,模拟了文本编辑器中的打字效果。
本来我想的是直接使用::after,后来发现,这种方法只适用于纯文本格式,但是如果出现了嵌套的情况,比如说div中嵌套了多个p的情况下,光标只能跟随在最后一个元素的下一行,因此我便采用了另一种方法。
话不多比直接开始
效果如下:
1、首先定义两个元素,一个用来生成文本的div,一个光标。
<div id="loadevent" class="loadevents"></div>
<div class="cursor"></div>
样式随便写写
.loadevents {
color: red;
border: #FF0000 solid 1px;
box-shadow: 5px 5px 15px 15px crimson;
}
.cursor{
position: absolute;
top: 15px;
left: 15px;
width: 10px;
height: 10px;
border-radius: 50%;
background: #FF0000;
}
2、划分功能实现的步骤,
1)首先我们需要找到最后一个文本节点。
2)在最后一个文本节点的尾部追加一个文字。
3)拿到我们追加的文字的位置
4)把光标设置到文字的位置
5)删除文字
3、接下来就是按照步骤去操作了!
//获取到文字展示的div
const loadevents = document.querySelector("#loadevent");
//光标
const cursor = document.querySelector(".cursor");
// 想展示的文字
var loadeventContent =
`1、问题背景
在软件开发过程中,版本控制是一个至关重要的环节。
原因:开发误操作上传Jar包,导致项目变大,后面就算删除jar,但是有commit记录,依旧导致文件过大。`;
//将文字中的换行替换为p标签,模拟现实场景
let arr = loadeventContent.split("\n").map(item => `<p>${item}</p>`).join("");
var count = 0;
var Timer = setInterval(changeContent, 100);
//文字展示的逻辑
function changeContent() {
loadevents.innerHTML = arr.substring(0, count);
count++;
updateCursor();
if (count == arr.length + 1) {
clearInterval(Timer);
}
}
//重点 获取最后一个元素
function getLastTextNode(node) {
if (node.nodeType === Node.TEXT_NODE) {//判断是不是文本类型
return node;//是就直接返回
}
for (let i = node.childNodes.length - 1; i >= 0; i--) {//如果不是,那么就从最后一个子节点开始遍历,因为要找最后一个嘛。
const child = node.childNodes[i];
const result = getLastTextNode(child);//递归拿到最后一个子结点的最后一个文本节点
if (result) {
return result;//找到以后就返回
}
}
return null //没有就返回空
}
functionupdateCursor(){
//第一步 获取最后一个元素
var node = getLastTextNode(loadevents)
//第二步 追加一个字符
var temp = document.createTextNode("|")
if (node) {
//如果node有值,就把字符插入到文本后边
node.parentNode.insertBefore(temp, node.nextSibling);
} else {
//没有就换行
loadevents.appendChild(temp);
}
//第三步 拿到文字位置
//创建 Range 对象
const range = document.createRange();
//设置 Range 的开始和结束位置:
range.setStart(temp, 0);
range.setEnd(temp, 0);
//获取 Range 的边界框:
const rect = range.getBoundingClientRect();
//获取父元素的边界框:
const texeRect = loadevents.getBoundingClientRect();
//计算光标相对于父元素的位置:
const left = rect.left - texeRect.left;
const top = rect.top - texeRect.top;
//第四步 设置光标位置
cursor.style.transform = `translate(${left}px, ${top}px)`;
//第五步 删除文字
temp.remove();
}
结束结束!
下边是完整代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>JavaScript实现字体依次出现循环播放效果</title>
<style type="text/css">
.loadevents {
color: red;
border: #FF0000 solid 1px;
box-shadow: 5px 5px 15px 15px crimson;
}
.cursor{
position: absolute;
top: 15px;
left: 15px;
width: 10px;
height: 10px;
border-radius: 50%;
background: #FF0000;
}
</style>
</head>
<body>
<div id="loadevent" class="loadevents"></div>
<div class="cursor"></div>
<script type="text/javascript">
const loadevents = document.querySelector("#loadevent");
const cursor = document.querySelector(".cursor");
var loadeventContent =
`1、问题背景
在软件开发过程中,版本控制是一个至关重要的环节。
原因:开发误操作上传Jar包,导致项目变大,后面就算删除jar,但是有commit记录,依旧导致文件过大。`;
let arr = loadeventContent.split("\n").map(item => `<p>${item}</p>`).join("");
console.log(cursor);
var count = 1;
var Timer = setInterval(changeContent, 100);
function changeContent() {
loadevents.innerHTML = arr.substring(0, count);
count++;
updateCursor();
if (count == arr.length + 1) { // +1 to account for the cursor
clearInterval(Timer);
}
}
function getLastTextNode(node) {
if (node.nodeType === Node.TEXT_NODE) {
return node;
}
// console.log(node.childNodes);
for (let i = node.childNodes.length - 1; i >= 0; i--) {
const child = node.childNodes[i];
const result = getLastTextNode(child);
if (result) {
return result;
}
}
return null
}
function updateCursor() {
// 获取最后一个元素
var node = getLastTextNode(loadevents)
// 追加文字
var temp = document.createTextNode("|")
if (node) {
node.parentNode.insertBefore(temp, node.nextSibling);
} else {
loadevents.appendChild(temp); // Add cursor at the end if no text node is found
}
// 拿到文字位置
const range = document.createRange();
range.setStart(temp, 0); // Start of the cursor
range.setEnd(temp, 0); // End of the cursor
const rect = range.getBoundingClientRect();
const texeRect = loadevents.getBoundingClientRect();
const left = rect.left - texeRect.left;
const top = rect.top - texeRect.top;
//设置光标位置
cursor.style.transform = `translate(${left}px, ${top}px)`;
// y移除数显
temp.remove();
}
</script>
</body>
</html>