高阶函数之光标跟随

如上图:模仿chatgpt 光标文字输出效果
技术要点
1、模仿chatgpt 打字输出效果 循环中使用异步等待 每隔一段时间输出一段文字 代码如下:
async function autoAppend(){
function transfer(text){
let div = document.createElement('div');
let p = document.createElement('p');
p.textContent = text;
div.appendChild(p)
return div.innerHTML;
}
function delay(time){
return new Promise(resolve => setTimeout(resolve, time));
}
const content = `音频和视频
文件的元
数据可能包含一些
信息如标题、艺术家、专辑等。然而,文件的创建时间通常不存在文件元数据中,而是由文件系统处理
`;
for(let i = 0;i<content.length;i++){
let text = content.slice(0,i);
let result = transfer(text);
//每隔一段时间更新下光标位置
textElem.innerHTML = result
updateCursor();
//每隔一段时间添加一个字
//循环中使用异步等待
await delay(300)
}
}
2、找到最后一个文本节点
function getLastText(node){
if(node.nodeType == Node.TEXT_NODE){
return node;
}
let childNodes = node.childNodes;
for(let i = childNodes.length - 1;i>=0;i--){
let result = getLastText(childNodes[i]);
if(result){
return result;
}
}
return null;
}
3、在最后一个文本节点加文字
let lastText = getLastText(textElem);
const textNode= document.createTextNode('标');
//加文字
if(lastText){
lastText.parentNode.appendChild(textNode);
}else{
textElem.appendChild(textNode)
}
4、根据最后一个文字设置光标位置
//根据最后一个文字设置光标位置
const range = document.createRange();
range.setStart(textNode,0)
range.setEnd(textNode,0);
const rect = range.getBoundingClientRect();
const textRect = contaier.getBoundingClientRect();
const x = rect.left - textRect.left;
const y = rect.top - textRect.top;
cursor.style.transform = `translate(${x}px,${y}px)`;
5、删除文字占位符
textNode.remove()
完整代码如下:
<!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>
.textContainer{
position: relative;
width: 800px;
height: 800px;
border: 1px solid #ddd;
margin: 0 auto;
white-space: pre-wrap;
}
.cursor{
position: absolute;
width: 10px;
height: 10px;
/* 添加 transform: translate(0,0) 是指元素当前位置上不做任何移动 在二维空间内对元素进行移动, 但他的移动是针对当前元素的位置进行移动 而不是基于其定位属性移动 */
/* 配置上 top 就是基于定位属性移动了 而不是针对当前元素的位置进行移动*/
transform: translate(0,0);
background: #000;
border-radius: 50%;
animation: blink .5s infinite;
top:5px;
left: 2px;
}
@keyframes blink{
0% {opacity: 1;}
50% {opacity: 0;}
100% {opacity: 1;}
}
</style>
</head>
<body>
<div class="textContainer">
<div class="text"></div>
<div class="cursor"></div>
</div>
<script>
//1、找到最后一个文本节点
//2、加文字
//3、根据文字设置光标位置
//4、删除文字
const contaier = document.querySelector('.textContainer')
const textElem = document.querySelector('.text');
const cursor = document.querySelector('.cursor');
async function autoAppend(){
function transfer(text){
let div = document.createElement('div');
let p = document.createElement('p');
p.textContent = text;
div.appendChild(p)
return div.innerHTML;
}
function delay(time){
return new Promise(resolve => setTimeout(resolve, time));
}
const content = `音频和视频
文件的元
数据可能包含一些
信息如标题、艺术家、专辑等。然而,文件的创建时间通常不存在文件元数据中,而是由文件系统处理
`;
for(let i = 0;i<content.length;i++){
let text = content.slice(0,i);
let result = transfer(text);
//每隔一段时间更新下光标位置
textElem.innerHTML = result
updateCursor();
//每隔一段时间添加一个字
//循环中使用异步等待
await delay(300)
}
}
autoAppend();
function getLastText(node){
if(node.nodeType == Node.TEXT_NODE){
return node;
}
let childNodes = node.childNodes;
for(let i = childNodes.length - 1;i>=0;i--){
let result = getLastText(childNodes[i]);
if(result){
return result;
}
}
return null;
}
function updateCursor(){
//1、找到最后一个文本节点
//2、加文字
//3、根据最后一个文字设置光标位置
//4、删除文字
let lastText = getLastText(textElem);
const textNode = document.createTextNode('袁');
//加文字
if(lastText){
lastText.parentNode.appendChild(textNode);
}else{
textElem.appendChild(textNode)
}
//根据最后一个文字设置光标位置
const range = document.createRange();
range.setStart(textNode,0)
range.setEnd(textNode,0);
const rect = range.getBoundingClientRect();
const textRect = contaier.getBoundingClientRect();
const x = rect.left - textRect.left;
const y = rect.top - textRect.top;
cursor.style.transform = `translate(${x}px,${y}px)`;
//删除文字
textNode.remove();
}
</script>
</body>
</html>

浙公网安备 33010602011771号