当前位置: 首页 > article >正文

使用 Vue 3 和 TypeScript 实现带打字效果的仿 AI 分析展示组件

在这篇博客中,我将分享如何用 Vue 3 和 TypeScript 实现一个带打字效果的 AI 分析展示组件。该组件具有如下功能:

  • 动态打字效果:模拟打字机逐步显示内容。
  • 自动滚动:内容超出容器高度时自动滚动到最新位置。

1. 组件实现需求

我们需要实现一个点击触发获取 AI 分析结果的功能,并动态显示该分析内容,同时实现以下功能:

  • 打字效果:AI 分析内容逐字展示,模仿打字机效果。
  • 自动滚动:内容过长时,滚动条自动跟随展示最新的内容。

2. 组件功能分析

(1) 打字效果的实现

我们将 AI 分析结果通过定时器逐步显示,模拟打字机效果。每次定时器触发时,向内容中添加一批字符。

3. 代码实现

模板部分

在模板部分,我们定义了一个包含标题和内容的组件。点击标题后触发获取 AI 分析结果,并以打字效果显示内容。加载状态用 Element Plus 提供的 Loading 图标进行展示:

<template>
  <div class="m_ai_analysis_title" @click="fetchAiAnalysis">
    <span class="iconfont icon-ai_icon m_ai_icon"></span>
    <h3>{{ title }}</h3>
    <el-icon v-if="aiLoading" class="is-loading">
      <Loading />
    </el-icon>
  </div>
  <div class="m_ai_analysis" v-if="printedContent">
    <div class="content-container" ref="contentContainer">
      <div class="typing-effect" v-html="printedContent"></div>
    </div>
  </div>
</template>
样式部分

为了实现打字机的视觉效果,我们通过 CSS 制作了光标闪烁效果,并设置了内容容器的滚动条样式:

.m_ai_analysis {
  padding: 12px 16px;
  border-top: 1px solid #eaeaea;
  background-color: #f9f9f9;
  border-radius: 8px;
}

.m_ai_analysis_title {
  margin-top: 20px;
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
}

.m_ai_analysis_title:hover .m_ai_icon {
  color: var(--el-color-primary);
  transform: scale(1.28);
  opacity: 0.88;
}

h3 {
  font-size: 16px;
  font-weight: bold;
}

.m_ai_icon {
  font-size: 28px;
  color: var(--el-color-primary);
  transition: all 0.3s ease;
}

.m_ai_icon.disabled {
  cursor: not-allowed;
  color: #ccc;
}

.content-container {
  max-height: 400px;
  overflow-y: auto;
}

.typing-effect {
  white-space: pre-wrap;
  display: inline-block;
  position: relative;
}

.typing-effect::after {
  content: '|';
  position: relative;
  font-size: 1.2em;
  color: black;
  animation: blink 1s step-end infinite;
}

@keyframes blink {
  0%, 100% {
    opacity: 1;
  }
  50% {
    opacity: 0;
  }
}
核心逻辑部分

script 部分,我们使用 Vue 3 的 Composition API 和 TypeScript 来定义组件的核心功能:

<script setup lang="ts">
import commonApi from '@/api/commonApi';
import { Loading } from '@element-plus/icons-vue';
import { ref, watch, nextTick, onBeforeUnmount } from 'vue';

const props = defineProps<{
  title: string;
  questionId: string | number | null;
}>();

const printedContent = ref(''); // 打印内容
const aiLoading = ref(false);   // 加载状态
const contentContainer = ref<HTMLElement | null>(null); // 滚动容器
let intervalId: ReturnType<typeof setInterval> | null = null; // 记录 intervalId

// 监听 questionId 变化,清空内容
watch(() => props.questionId, async () => {
  printedContent.value = ''; 
});

// 获取 AI 分析结果
const fetchAiAnalysis = async () => {
  if (!props.questionId) return;

  aiLoading.value = true;
  printedContent.value = '';
  try {
    const { data } = await commonApi.getGptAnalysis({ questionId: props.questionId });
    if (data) {
      await typeContent(data);
    }
  } catch (error) {
    console.error('Error fetching AI analysis:', error);
  } finally {
    aiLoading.value = false;
  }
};

// 打字效果实现
const typeContent = async (content: string) => {
  let index = 0;
  const batchSize = 5; // 每次显示的字符数量

  intervalId = setInterval(async () => {
    if (index < content.length) {
      const nextChunk = content.slice(index, index + batchSize);
      printedContent.value += nextChunk;
      index += batchSize;
      await nextTick();
      scrollToBottom();
    } else {
      clearInterval(intervalId!);
    }
  }, 100); 
};


// 自动滚动到容器底部
const scrollToBottom = () => {
  if (contentContainer.value) {
    requestAnimationFrame(() => {
      contentContainer.value!.scrollTop = contentContainer.value!.scrollHeight;
    });
  }
};

// 清理定时器
onBeforeUnmount(() => {
  if (intervalId) {
    clearInterval(intervalId);
  }
});
</script>

4. 关键技术点解析

(1) 打字效果

使用 setInterval 每隔一定时间添加一部分字符,直到整个内容被打印完成。这种方式模拟了一个打字机的效果,用户可以逐步看到分析内容的生成过程。

(2) 滚动到最新内容

通过 requestAnimationFrame 实现平滑的滚动效果,使得当新内容被添加时,页面自动滚动到底部,保持用户可以看到最新的内容。

效果图:

5. 总结

本文分享了如何在 Vue 3 和 TypeScript 项目中实现一个带打字效果的 AI 分析展示组件。


http://www.kler.cn/news/309576.html

相关文章:

  • 【C/C++语言系列】指针数组、数组指针、函数声明和函数指针区别
  • Git 中的refs
  • Python异常处理:自定义异常②
  • 智慧体育场馆:科技引领未来运动体验
  • 【C语言进阶】动态内存与柔性数组:C语言开发者必须知道的陷阱与技巧
  • JAVA学习笔记01-变量的初始化
  • Medieval Fantasy Town Village Environment for RPG FPS 中世纪城镇环境
  • 时序数据库 TDengine 的入门体验和操作记录
  • 某oa命令执行漏洞挖掘思路
  • 网络安全。
  • 数学建模笔记——动态规划
  • Vue3中集成高德地图并实现平移缩放功能
  • 如何搭建一个ip池用来做数据抓取用
  • MFC工控项目实例之十四模拟量信号名称从文件读写
  • uniapp上使用document方案之renderjs
  • 回收站数据怎么恢复?用这 5 种方法,准能恢复回收站数据!
  • 基于SpringBoot+定时任务实现地图上绘制车辆实时运动轨迹图
  • Pr:Adobe SRT
  • React中forwardRef()的作用?
  • python多进程程序设计 之二
  • 基于深度学习的文本引导的图像编辑
  • 【我的 PWN 学习手札】Largebin Attack(< glibc-2.30)
  • linux-Shell 编程-Shell 脚本基础
  • lvs命令介绍
  • k8s部署jumpserver4.0.2
  • Redis(redis基础,SpringCache,SpringDataRedis)
  • golang学习笔记24——golang微服务中配置管理问题的深度剖析
  • Unity3D 游戏数据本地化存储与管理详解
  • 深度智能:迈向高级时代的人工智能
  • ELK 日志分析