🔥 Vue3 实现超丝滑打字机效果组件(可复用、高定制)
在前端开发中,打字机效果能极大提升页面的交互趣味性和视觉体验,比如 AI 聊天回复、个性化介绍页等场景都非常适用。本文将分享一个基于 Vue3 + Composition API 开发的高性能、高定制化打字机组件,支持打字/删除循环、光标闪烁、自定义样式等核心功能,且代码结构清晰、易于扩展。
🎯 组件特性
- ✅ 自定义打字速度、删除速度、循环延迟
- ✅ 支持光标显示/隐藏、闪烁效果开关
- ✅ 打字完成后自动删除(可选)+ 循环播放
- ✅ 完全自定义样式(字体、颜色、大小等)
- ✅ 暴露控制方法(开始/暂停),支持手动干预
- ✅ 无第三方依赖,纯原生 Vue3 实现
- ✅ 性能优化:组件卸载自动清理定时器,避免内存泄漏

📝 完整组件代码
js
<template>
<div class="typewriter-container" :style="fontsConStyle">
<!-- 打字文本 - 逐字符渲染 -->
<span class="typewriter-text">
<span
v-for="(char, index) in displayedText"
:key="index"
class="character"
:data-index="index"
>
{{ char }}
</span>
</span>
<!-- 光标 - 精准控制显示/闪烁 -->
<span
v-if="showCursor && isCursorVisible"
class="cursor"
:class="{ 'blink': showBlinkCursor }"
aria-hidden="true"
/>
</div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount, computed, watch, watchEffect } from "vue";
// 组件 Props 定义(带完整校验)
const props = defineProps({
// 要显示的文本内容
text: {
type: String,
required: true,
default: ""
},
// 打字速度(ms/字符)
speed: {
type: Number,
default: 80,
validator: (value) => value > 0
},
// 是否显示光标
showCursor: {
type: Boolean,
default: true
},
// 光标是否闪烁
blinkCursor: {
type: Boolean,
default: true
},
// 是否自动开始打字
autoStart: {
type: Boolean,
default: true
},
// 是否循环播放
loop: {
type: Boolean,
default: false
},
// 循环延迟(打字完成后等待时间)
loopDelay: {
type: Number,
default: 1000,
validator: (value) => value >= 0
},
// 容器样式(自定义字体、颜色等)
fontsConStyle: {
type: Object,
default: () => ({
fontSize: "2rem",
fontFamily: "'Courier New', monospace",
color: "#333",
lineHeight: "1.5"
})
},
// 是否开启删除效果
deleteEffect: {
type: Boolean,
default: false
},
// 删除速度(ms/字符)
deleteSpeed: {
type: Number,
default: 30,
validator: (value) => value > 0
},
// 字符入场动画开关
charAnimation: {
type: Boolean,
default: true
}
});
// 响应式状态
const displayedText = ref(""); // 当前显示的文本
const currentIndex = ref(0); // 当前字符索引
const isPlaying = ref(false); // 是否正在播放
const isDeleting = ref(false); // 是否正在删除
const isCursorVisible = ref(true);// 光标是否显示
// 定时器标识(用于清理)
let intervalId = null;
let timeoutId = null;
let cursorTimer = null;
// 计算属性:控制光标闪烁状态
const showBlinkCursor = computed(() => {
return props.blinkCursor &&
!isDeleting.value &&
(displayedText.value.length === props.text.length || displayedText.value.length === 0);
});
// 工具函数:清除所有定时器
const clearAllTimers = () => {
if (intervalId) clearInterval(intervalId);
if (timeoutId) clearTimeout(timeoutId);
if (cursorTimer) clearInterval(cursorTimer);
intervalId = null;
timeoutId = null;
cursorTimer = null;
};
// 核心方法:开始打字
const startTyping = () => {
// 重置状态
clearAllTimers();
isPlaying.value = true;
isDeleting.value = false;
currentIndex.value = 0;
displayedText.value = "";
isCursorVisible.value = true;
// 打字逻辑
intervalId = setInterval(() => {
if (currentIndex.value < props.text.length) {
displayedText.value = props.text.substring(0, currentIndex.value + 1);
currentIndex.value++;
} else {
// 打字完成
clearInterval(intervalId);
isPlaying.value = false;
// 循环逻辑
if (props.loop) {
if (props.deleteEffect) {
timeoutId = setTimeout(startDeleting, props.loopDelay);
} else {
timeoutId = setTimeout(startTyping, props.loopDelay);
}
}
}
}, props.speed);
};
// 核心方法:开始删除
const startDeleting = () => {
clearAllTimers();
isDeleting.value = true;
intervalId = setInterval(() => {
if (currentIndex.value > 0) {
currentIndex.value--;
displayedText.value = props.text.substring(0, currentIndex.value);
} else {
// 删除完成
clearInterval(intervalId);
isDeleting.value = false;
// 循环打字
if (props.loop) {
timeoutId = setTimeout(startTyping, props.loopDelay);
} else {
isCursorVisible.value = false; // 非循环模式下删除完成隐藏光标
}
}
}, props.deleteSpeed);
};
// 监听文本变化:自动重启打字(适配动态文本场景)
watch(() => props.text, (newText) => {
if (newText && props.autoStart) {
startTyping();
}
}, { immediate: true });
// 初始化光标闪烁(非闪烁模式下固定显示)
watchEffect(() => {
if (props.showCursor && !cursorTimer) {
cursorTimer = setInterval(() => {
if (!showBlinkCursor.value) {
isCursorVisible.value = true;
} else {
isCursorVisible.value = !isCursorVisible.value;
}
}, 500);
}
});
// 组件挂载:自动开始打字
onMounted(() => {
if (props.autoStart && props.text) {
startTyping();
}
});
// 组件卸载:清理所有定时器(避免内存泄漏)
onBeforeUnmount(() => {
clearAllTimers();
});
// 暴露组件方法(供父组件调用)
defineExpose({
start: startTyping, // 手动开始
pause: clearAllTimers, // 暂停
restart: () => { // 重启
clearAllTimers();
startTyping();
},
isPlaying, // 当前播放状态
isDeleting // 当前删除状态
});
</script>
<style scoped>
/* 容器样式 - 适配行内/块级显示 */
.typewriter-container {
display: inline-flex;
align-items: center;
position: relative;
font-size: inherit;
line-height: inherit;
font-family: inherit;
white-space: pre-wrap; /* 支持换行符 */
word-break: break-all; /* 防止长文本溢出 */
}
/* 文本容器 */
.typewriter-text {
display: inline;
font-size: inherit;
line-height: inherit;
font-family: inherit;
color: inherit;
}
/* 字符样式 - 入场动画 */
.character {
display: inline-block;
animation: typeIn 0.1s ease-out forwards;
opacity: 0;
}
/* 字符入场动画 */
@keyframes typeIn {
0% {
transform: translateY(5px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
/* 光标样式 - 垂直居中优化 */
.cursor {
display: inline-block;
width: 2px;
height: 1.2em; /* 匹配字体高度 */
background-color: currentColor;
margin-left: 2px;
vertical-align: middle;
position: relative;
top: 0;
opacity: 1;
}
/* 光标闪烁动画 */
.cursor.blink {
animation: blink 1s infinite step-end;
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
/* 禁用字符动画的样式 */
:deep(.character) {
animation: none !important;
opacity: 1 !important;
}
</style>
🚀 核心优化点说明
1. 功能增强
- 新增
charAnimation属性:可开关字符入场动画,适配不同场景 - 支持动态文本:监听
text属性变化,文本更新自动重启打字 - 优化光标逻辑:非闪烁模式下光标固定显示,避免闪烁干扰
- 新增
restart方法:支持手动重启打字效果 - 文本换行支持:添加
white-space: pre-wrap,兼容带换行符的文本
2. 性能优化
- 定时器统一管理:所有定时器集中清理,避免内存泄漏
- 减少不必要渲染:通过
watchEffect精准控制光标定时器创建/销毁 - 样式优化:使用
currentColor继承文本颜色,光标颜色与文本一致 - 边界处理:添加
word-break: break-all,防止长文本溢出
3. 代码健壮性
- 完善 Prop 校验:所有数值类型添加范围校验,避免非法值
- 状态重置:每次开始打字前重置所有状态,避免多轮执行冲突
- 注释完善:关键逻辑添加注释,提升代码可读性
📖 使用示例
基础使用
js
<template>
<Typewriter
text="Hello Vue3! 这是一个超丝滑的打字机效果组件✨"
speed="50"
/>
</template>
<script setup>
import Typewriter from './components/Typewriter.vue';
</script>
高级使用(循环+删除效果)
js
<template>
<div>
<Typewriter
ref="typewriterRef"
text="Vue3 打字机组件 | 支持循环删除 | 自定义样式"
:speed="60"
:deleteSpeed="40"
:loop="true"
:deleteEffect="true"
:loopDelay="1500"
:fontsConStyle="{
fontSize: '1.5rem',
color: '#409eff',
fontFamily: '微软雅黑'
}"
/>
<button @click="handleRestart">重启打字</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
import Typewriter from './components/Typewriter.vue';
const typewriterRef = ref();
// 手动重启打字
const handleRestart = () => {
typewriterRef.value.restart();
};
</script>
🎨 样式自定义说明
| 属性 | 说明 | 默认值 |
|---|---|---|
| fontSize | 字体大小 | 2rem |
| fontFamily | 字体 | 'Courier New', monospace |
| color | 文本颜色 | #333 |
| lineHeight | 行高 | 1.5 |
你可以通过 fontsConStyle 属性完全自定义组件样式,例如:
js
fontsConStyle: {
fontSize: "18px",
color: "#e6a23c",
fontWeight: "bold",
background: "#f5f7fa",
padding: "10px 15px",
borderRadius: "8px"
}
🛠️ 扩展方向
- 自定义字符动画:通过 Prop 传入动画类名,支持不同的字符入场效果
- 分段打字:支持数组形式的文本,分段打字+间隔
- 速度渐变:实现打字速度由快到慢/由慢到快的效果
- 暂停/继续:扩展暂停后继续打字的功能(记录当前索引)
- 结合 AI 流式响应:对接 AI 接口的流式返回,实时更新打字文本
📌 总结
这个打字机组件基于 Vue3 Composition API 开发,具备高复用性、高定制性的特点,核心优化点如下:
- 完善的定时器管理,避免内存泄漏
- 精准的状态控制,支持打字/删除/循环全流程
- 灵活的样式自定义,适配不同业务场景
- 暴露控制方法,支持父组件手动干预
组件可直接集成到 Vue3 项目中,适用于 AI 聊天、个人主页、产品介绍等需要打字机效果的场景,开箱即用!