高阶函数之光标跟随

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

  

 
posted @ 2024-07-25 14:28  飞奔的龟龟  阅读(15)  评论(0编辑  收藏  举报