在很多聊天机器人、智能客服或对话类应用中,"打字效果" 是一个非常实用的交互细节。它能模拟真实的输入过程,让用户感知到 "对方正在回复",提升交互体验。本文将基于 Vue 框架,详细讲解如何实现这一效果,并提供可直接复用的代码方案。

一、实现思路:核心原理拆解

打字效果的本质是将完整文本按顺序逐字符(或逐片段)展示,并通过定时器控制展示间隔,模拟人打字的节奏。具体到 Vue 中,需要解决三个核心问题:

  1. 文本拆分与拼接:将待展示的完整内容拆分成最小单元(通常是单个字符),通过循环逐步拼接
  2. 节奏控制:使用定时器(setTimeout)控制每次拼接的间隔时间,形成打字速度
  3. DOM 同步与滚动:每次文本更新后,确保 DOM 及时渲染并自动滚动到最新内容

二、完整实现:Vue 组件代码

下面是一个可直接使用的 Vue 组件,包含了打字效果的完整逻辑,以及对话列表的基础样式:

<template>
  <div class="chat-container">
    <!-- 对话列表容器 -->
    <div class="chat-list" ref="chatList">
      <div 
        v-for="(item, index) in chatList" 
        :key="index" 
        class="chat-item"
        :class="{ 'user-message': item.isUser }"
      >
        <div class="avatar">{{ item.isUser ? '我' : 'Bot' }}</div>
        <div class="message">{{ item.content }}</div>
      </div>
    </div>

    <!-- 输入区域 -->
    <div class="input-area">
      <input 
        v-model="userInput" 
        type="text" 
        placeholder="输入消息..."
        @keyup.enter="sendMessage"
      >
      <button @click="sendMessage">发送</button>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      chatList: [], // 对话列表
      userInput: '', // 用户输入内容
      typingTimer: null // 打字定时器(用于清除)
    };
  },
  methods: {
    // 发送用户消息
    sendMessage() {
      if (!this.userInput.trim()) return;
      
      // 添加用户消息到列表
      this.chatList.push({
        isUser: true,
        content: this.userInput.trim()
      });
      this.userInput = '';
      this.scrollToBottom();

      // 模拟机器人回复(实际项目中可替换为接口请求)
      setTimeout(() => {
        this.generateBotReply();
      }, 500);
    },

    // 生成机器人回复并触发打字效果
    generateBotReply() {
      // 模拟回复内容(实际项目中从接口获取)
      const fullReply = "你好!这是一条模拟的机器人回复,正在通过打字效果逐字展示。在实际应用中,你可以将这里的内容替换为接口返回的真实数据。";
      
      // 将回复拆分成单个字符的数组(核心:拆分文本)
      const replyChars = fullReply.split('');
      
      // 添加一个空消息占位,用于后续打字填充
      this.chatList.push({
        isUser: false,
        content: ''
      });
      
      // 开始打字效果(传入目标索引和字符数组)
      const targetIndex = this.chatList.length - 1;
      this.startTyping(targetIndex, replyChars);
    },

    // 核心:打字效果实现
    startTyping(targetIndex, chars) {
      let currentIndex = 0; // 当前要显示的字符索引
      
      // 清除可能存在的旧定时器(避免冲突)
      if (this.typingTimer) {
        clearTimeout(this.typingTimer);
      }

      // 递归函数:逐字符展示
      const typeNext = () => {
        if (currentIndex < chars.length) {
          // 拼接当前字符(更新对话内容)
          this.chatList[targetIndex].content += chars[currentIndex];
          currentIndex++;
          
          // 继续定时调用(控制打字速度,50ms/字符)
          this.typingTimer = setTimeout(typeNext, 50);
          
          // 每次更新后滚动到底部
          this.scrollToBottom();
        } else {
          // 打字结束,清除定时器
          clearTimeout(this.typingTimer);
        }
      };

      // 启动打字
      typeNext();
    },

    // 自动滚动到最新消息
    scrollToBottom() {
      // 等待DOM更新后执行滚动(关键:确保内容已渲染)
      this.$nextTick(() => {
        const chatListEl = this.$refs.chatList;
        if (chatListEl) {
          chatListEl.scrollTop = chatListEl.scrollHeight;
        }
      });
    }
  },
  beforeDestroy() {
    // 组件销毁前清除定时器,避免内存泄漏
    clearTimeout(this.typingTimer);
  }
};
</script>

<style scoped>
.chat-container {
  width: 500px;
  margin: 20px auto;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  overflow: hidden;
}

.chat-list {
  height: 400px;
  padding: 15px;
  overflow-y: auto;
  background-color: #f9fafb;
}

.chat-item {
  display: flex;
  margin-bottom: 15px;
  max-width: 80%;
}

/* 区分用户和机器人消息的对齐方式 */
.chat-item:not(.user-message) {
  align-self: flex-start;
}
.chat-item.user-message {
  margin-left: auto;
  flex-direction: row-reverse;
}

.avatar {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background-color: #e5e7eb;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  margin-right: 10px;
}

.chat-item.user-message .avatar {
  margin-right: 0;
  margin-left: 10px;
  background-color: #3b82f6;
  color: white;
}

.message {
  padding: 8px 12px;
  border-radius: 18px;
  background-color: white;
  box-shadow: 0 1px 2px rgba(0,0,0,0.1);
}

.chat-item.user-message .message {
  background-color: #3b82f6;
  color: white;
}

.input-area {
  display: flex;
  padding: 10px;
  border-top: 1px solid #e5e7eb;
}

.input-area input {
  flex: 1;
  padding: 8px 12px;
  border: 1px solid #e5e7eb;
  border-radius: 20px;
  outline: none;
}

.input-area button {
  margin-left: 10px;
  padding: 8px 16px;
  background-color: #3b82f6;
  color: white;
  border: none;
  border-radius: 20px;
  cursor: pointer;
}
</style>

总结

Vue 中实现对话打字效果的核心是利用定时器逐字符更新文本,并通过 $nextTick 确保 DOM 同步。本文提供的方案不仅包含基础打字逻辑,还兼顾了用户体验(自动滚动)和代码健壮性(定时器管理),可直接应用于聊天机器人、智能客服等场景。根据实际需求调整拆分策略和打字速度,就能实现更贴近真实场景的交互效果。

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐