vue实现即开即用的AI对话打字机效果

废话不多说 直接上代码 读props 来引入组件即可

javascript 复制代码
<template>
  <div class="typewriter-text">
    {{ displayedText }}<span class="cursor">|</span>
  </div>
</template>

<script setup>
import { ref, watch, onMounted, onUnmounted } from 'vue'

const props = defineProps({
  // 完整的文字
  text: {
    type: String,
    default: ''
  },
  // 打字的速度
  speed: {
    type: Number,
    default: 50
  },
  // 是否自动开始
  autoStart: {
    type: Boolean,
    default: true
  },
  // 是否可见
  visible: {
    type: Boolean,
    default: true
  }
})

const displayedText = ref('')
const typewriterTimer = ref(null)

function startTypewriter () {
  displayedText.value = ''
  let index = 0
  if (typewriterTimer.value) {
    clearInterval(typewriterTimer.value)
  }
  typewriterTimer.value = setInterval(() => {
    if (index < props.text.length) {
      displayedText.value += props.text[index]
      index++
    } else {
      if (typewriterTimer.value) {
        clearInterval(typewriterTimer.value)
        typewriterTimer.value = null
      }
    }
  }, props.speed)
}

function resetTypewriter () {
  if (typewriterTimer.value) {
    clearInterval(typewriterTimer.value)
    typewriterTimer.value = null
  }
  displayedText.value = ''
}

// 监听visible,控制打字机效果的开始和重置
watch(() => props.visible, (newVal) => {
  if (newVal) {
    // autoStart为true的时候自动开始
    if (props.autoStart && props.text) {
      resetTypewriter()
      startTypewriter()
    }
  } else {
    // 重置
    resetTypewriter()
  }
}, { immediate: true })

// 监听文字,文字改变后重新开始
watch(() => props.text, () => {
  if (props.autoStart && props.visible && props.text) {
    resetTypewriter()
    startTypewriter()
  }
}, { immediate: false })

onMounted(() => {
  if (props.autoStart && props.visible && props.text) {
    startTypewriter()
  }
})

onUnmounted(() => {
  resetTypewriter()
})

defineExpose({
  start: startTypewriter,
  reset: resetTypewriter
})
</script>

<style lang="less" scoped>
.typewriter-text {
  font-size: 14px;
  line-height: 1.8;
  color: #333;
  min-height: 100px;
  white-space: pre-wrap;
  word-wrap: break-word;
}

.typewriter-text .cursor {
  display: inline-block;
  margin-left: 2px;
  animation: blink 1s infinite;
  color: #3b82f6;
  font-weight: bold;
}

@keyframes blink {
  0%, 50% {
    opacity: 1;
  }
  51%, 100% {
    opacity: 0;
  }
}
</style>
相关推荐
kyriewen6 小时前
面试官让我用 AI 重构一个 8 年陈的 React 组件——他说他不看代码,只看我会不会拆
前端·javascript·面试
计算机魔术师6 小时前
700吉瓦「幽灵用电」:美国AI数据中心的抢电闹剧正在失控
前端
IT_陈寒6 小时前
JavaScript数组排序踩的坑,差点让我加班到凌晨
前端·人工智能·后端
风骏时光牛马6 小时前
提示词工程:大模型交互的核心构建艺术
前端
今日无bug7 小时前
大模型的回答是怎么一个字一个字蹦出来的?前端流式输出解析
前端·llm·agent
计算机魔术师7 小时前
Anthropic 复盘 Claude 模型越权访问真实系统事件并改进对齐与安全措施
前端
掘金者阿豪7 小时前
Seedance 2.0/2.5 虚拟素材能跨 Key 共用吗?一次讲清 Asset ID、账号隔离与 SaaS 素材架构
前端·后端
Jacky19997 小时前
Electron + Vue 3 桌面打字游戏实战:从 VSCode 扩展到独立应用的架构改造
javascript
用户50093768390397 小时前
热敏小票 Web 打印:我在 58/80mm 上折腾的两周
前端·vue.js
XGM7 小时前
一个前端新手的"顿悟"时刻:DOM树、渲染原理和JS动态渲染
前端