在这篇博客中,我将分享如何用 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 分析展示组件。