实现GPT的光标跟随文本动画效果

1,008 阅读3分钟

光标跟随效果2_1.gif 在GPT回答问题的页面上,有一个小黑点会根据回答的内容一直跟随文本移动的一个效果。这种动画效果不仅使用户体验更加生动和直观,还增强了页面的互动性和吸引力。本文将详细介绍如何使用HTML、CSS和JavaScript来实现这个光标跟随文本动画效果的技术细节和实现过程。

1. 概述

光标跟随文本动画效果是指在文本逐步显示的过程中,光标(或其他自定义标记)会实时跟随文本内容移动,仿佛在输入的同时显示文本。这种效果常用于模拟打字机效果或增强页面的交互性。

2. HTML 结构

首先,我们来看一下HTML部分的结构:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>光标跟随效果</title>
    <style>
        /* CSS 样式在这里定义 */
    </style>
</head>
<body>
    <div class="content-box">
        <div class="text"></div>
        <div class="dot"></div>
    </div>

    <!-- JavaScript 部分在这里 -->
</body>
</html>

在这段HTML代码中,我们定义了一个.content-box容器,包含两个子元素:.text用于显示逐步展示的文本内容,.dot则是模拟光标的元素。

3. CSS 样式

接下来,我们为元素添加CSS样式,使其具有合适的布局和外观:

<style>
    .content-box {
        position: relative;
    }

    .text {
        background-color: #ccc;
        width: fit-content;
        padding: 5px;
        border-radius: 0 10px 10px 10px;
    }

    .dot {
        width: 10px;
        height: 10px;
        border-radius: 5px;
        background-color: black;
        position: absolute;
        top: 5px;
        animation: dotAnimation 0.5s infinite;
    }

    @keyframes dotAnimation {
        0% {
            opacity: 0;
        }

        100% {
            opacity: 1;
        }
    }
</style>

在这段CSS中,定义了.content-box容器的基本样式,包括背景色和边框圆角。.dot元素是一个黑色圆点,通过position: absolute;将其定位在.text元素的上方,并且通过CSS动画dotAnimation使其产生闪烁效果。

4. JavaScript 动态效果

最后,我们使用JavaScript来实现光标跟随文本动画的核心逻辑:

<script>
    const dot = document.querySelector('.dot'); // 获取光标元素
    const ctBox = document.querySelector('.content-box'); // 包含文本和光标的容器
    const txt = document.querySelector('.text'); // 文本内容的容器
    //定义一段文字
    const characters = `北国风光,千里冰封,万里雪飘。
望长城内外,惟余莽莽;大河上下,顿失滔滔。
山舞银蛇,原驰蜡象,欲与天公试比高。
须晴日,看红装素裹,分外妖娆。
江山如此多娇,引无数英雄竞折腰。
惜秦皇汉武,略输文采;唐宗宋祖,稍逊风骚。
一代天骄,成吉思汗,只识弯弓射大雕。
俱往矣,数风流人物,还看今朝。`; // 要显示的文本

    // 延时函数
    function delay(time) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve();
            }, time);
        });
    }

    // 格式化文本为段落格式
    function formatText(text) {
        return text.split('\n').map(e => `<p>${e}</p>`).join('');
    }

    // 获取最后一个文本节点
    function getLastText(node) {
        if (node.nodeType === Node.TEXT_NODE) {
            return node;
        }
        for (let i = node.childNodes.length - 1; i >= 0; i--) {
            let child = node.childNodes[i];
            let result = getLastText(child);
            if (result) {
                return result;
            }
        }
        return null;
    }

    // 开始显示文本
    async function start(delayTime) {
        for (let i = 0; i < characters.length; i++) {
            let content = characters.slice(0, i); // 逐步获取文本内容
            let result = formatText(content); // 格式化为段落格式
            txt.innerHTML = result; // 更新文本内容显示
            updateView(); // 更新光标位置
            await delay(delayTime); // 等待一段时间
            if (i == charactes.length - 1) {
                dot.remove();
            }
        }
    }

    // 更新光标位置
    function updateView() {
        let textNode = getLastText(txt); // 获取最后一个文本节点
        let tempNode = document.createTextNode('|'); // 创建一个临时文本节点表示光标位置
        if (textNode) {
            textNode.after(tempNode); // 插入临时节点在最后一个文本节点后
        } else {
            txt.appendChild(tempNode); // 如果没有文本节点,则直接添加
        }
        const range = document.createRange(); // 创建一个新的文本范围对象
        range.setStart(tempNode, 0); // 设置范围的起始位置为临时节点的开头
        range.setEnd(tempNode, 0); // 设置范围的结束位置为临时节点的开头(即光标位置)
        const rect = range.getBoundingClientRect(); // 获取新范围的边界位置信息
        const textContainer = ctBox.getBoundingClientRect(); // 获取文本容器的边界位置信息
        let left = rect.left - textContainer.left; // 计算光标的水平位置
        let top = rect.top - textContainer.top; // 计算光标的垂直位置
        dot.style.transform = `translate(${left}px,${top}px)`; // 设置光标的位置
        tempNode.remove(); // 移除临时节点
    }

    start(100); // 调用开始函数,每100毫秒显示一个字符
</script>

在这段JavaScript代码中,首先获取需要操作的DOM元素,包括文本容器.text、光标.dot以及包含它们的容器.content-box。然后,通过start()函数逐步显示定义的characters文本内容,并在每个字符显示后调用updateView()函数更新光标的位置。

5. 总结

通过结合HTML、CSS和JavaScript,成功实现了一个光标跟随文本动画效果。这种效果不仅增加了页面的视觉吸引力,还提升了用户与页面的交互性。你可以根据实际需求,进一步优化和扩展这个效果,例如增加更复杂的动画效果或改进文本显示方式。

通过本文的学习,希望你能够掌握如何利用前端技术实现类似的动态效果,为自己的Web项目增添更多创意和互动性。