# 光标跟随效果--前端实现

2024-01-23 19.45.19

<body>
  <div class="text-container">
    <div class="text"></div>
    <div class="cursor"></div>
  </div>
  <script type="text/javascript" src="./index.js"></script>
</body>

光标绘制

.cursor {
  display: inline-block;
  position: absolute;
  top: 0;
  left: 0;
  transform: translate(0px,0px);
  width: 10px;
  height: 10px;
  background-color: black;
  border-radius: 50%;
  animation: blinking 1s infinite;
}
@keyframes blinking {
  0% { opacity: 1; }
  50% { opacity: 0; }
  100% { opacity: 1; }
}

文本容器绘制

.text-container {
  border: 1px solid #ccc;
  border-radius: 5px;
  display: inline-block;
  position: relative;
  width: 100%;
  min-height: 100px;
}

思路:

  • 找到最后一个文本节点
  • js无法添加伪元素 在文本节点最后添加文字,获取文字在容器中的位置
  • 根据文字位置设置光标位置
  • 删除文字
const textContainer = document.querySelector('.text-container');
const textElem = document.querySelector('.text');
const cursor = document.querySelector('.cursor');

解析截取的内容,更新光标位置

for (let i = 0; i < content.length; i++) {
    //截取字符内容
    let text = content.slice(0, i);
    console.log(text,'==========');
    //转换接收的内容
    let result = transfer(text);
    //将转换结果显示
    textElem.innerHTML = result;
    //更新光标位置
    updateCursor();
    //添加延迟
    await delay(3);
}

延迟函数

function delay(duration) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve();
        }, duration);
    });
}

格式显示核心方法:transfer将接收的内容展示为HTML格式内容

这个函数很难写,示例代码有很多bug

function transfer(text) {
    // 去除多余的空格和换行符
    text = text.trim();

    // 段落格式化
    const paragraphs = text.split('\n\n');
    const formattedText = paragraphs.map(paragraph => {
        // 去除段落中的多余空格和换行符
        paragraph = paragraph.trim();

        // 提取代码段
        const codeBlockRegex = /```[\s\S]+?```/g;
        const codeBlocks = paragraph.match(codeBlockRegex);

        if (codeBlocks) {
            // 处理代码段
            const codeBlocksWithHighlighting = codeBlocks.map(codeBlock => {
                // 去除代码段的语法高亮标签(如果有的话)
                const languageRegex = /```(\w+)/;
                const language = codeBlock.match(languageRegex)[1];
                const code = codeBlock.replace(languageRegex, '').replace(/```/g, '');

                // 使用highlight.js库进行语法高亮处理(需要引入该库)
                const highlightedCode = hljs.highlight(language, code).value;
                return `<pre><code class="language-${language}">${highlightedCode}</code></pre>`;

            });

            // 将处理后的代码段插入段落中
            paragraph = paragraph.replace(codeBlockRegex, codeBlocksWithHighlighting.join(''));
        }

        // 添加段落标签
        return `<p>${paragraph}</p>`;
    }).join('');

    return formattedText;
}

改变光标位置:

getLastTextNode核心方法:获取最后一个文本节点

在文本节点最后追加一个文本,创建选区对象,选中 临时文本对象

选区对象-->获取文本对象浏览器视口坐标

移动光标元素到指定坐标,删除临时文本节点

function getLastTextNode(node) {
    //如果
    if(node.nodeType === Node.TEXT_NODE) {
        return node;
    }
    //不是文本节点,获取所有子节点,从最后一个节点循环
    let children = node.childNodes;
    for (let i = children.length - 1; i >= 0; i--) {
        let result = getLastTextNode(children[i]);
        if (result) {
            return result;
        }
    }
    return null;
}


function updateCursor() {
    //找到最后一个文本节点
    const lastTextNode = getLastTextNode(textElem);
    //加文字
    const tempText = document.createTextNode('*');

    if(lastTextNode) {
        //找到它的父节点
        //lastTextNode.parentNode.insertBefore(tempText,lastTextNode.nextSibling );
        lastTextNode.parentNode.appendChild(tempText);
    } else {
        textElem.appendChild(tempText);
    }
    //根据文字位置设置光标位置
    //tempText.getBoundingClientRect(); 节点没有该api
    const range = document.createRange();//表示选中文字的范围
    range.setStart(tempText,0);
    range.setEnd(tempText,0);
    const rect = range.getBoundingClientRect();
    // console.log(rect);//会打印文本的坐标,是相对于浏览器视口的
    //光标位置,绝对定位,相对于父元素定位
    //计算最后文字坐标在容器中的坐标
    const textRect = textContainer.getBoundingClientRect();
    const x = rect.left - textRect.left;
    const y = rect.top - textRect.top;
    // console.log(x,y,'========');
    //设置光标元素位置
    cursor.style.transform = `translate(${x}px, ${y}px)`;
    // console.log(cursor,'=======');
    console.log(`translate(${x}px, ${y}px)`);
    //删除文字
    tempText.remove();
}
更新时间: 2024年1月23日星期二晚上7点53分