高階函數之光標跟隨

如上圖:模仿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號