使用 Vue 3 实现打字机效果

在现代前端开发中,添加一些视觉效果可以提升用户体验。其中,打字机效果是一种常见且吸引人的效果,可以用于展示动态文本。本文将介绍如何在 Vue 3 中实现打字机效果。

实现步骤

1. 创建自定义指令

我们首先创建一个自定义指令 v-typewriter,用于实现打字机效果。这个指令将逐字显示绑定的文本内容。

javascript 复制代码
const typeWriter = ref(null);

const typewriterDirective = (el, binding) => {
  const indexValue = el.getAttribute('data-index');
  const delay = 150; // 设置延迟时间,默认150ms
  let i = 0;

  typeWriter.value = setInterval(() => {
    if (binding?.value && i < binding.value.length) {
      if (textList.value && textList.value[indexValue]) {
        textList.value[indexValue].typewriterText += binding.value.charAt(i) || '';
      }
      i++;
    } else {
      clearInterval(typeWriter.value);
      stop(textList.value[indexValue], indexValue, true);
    }
  }, delay);
};

const vTypewriter = {
  mounted(el, binding) {
    typewriterDirective(el, binding);
  }
};

2. 使用自定义指令

在 Vue 组件中使用自定义指令 v-typewriter。该指令会在元素挂载时自动触发,逐字显示文本内容。

javascript 复制代码
<template>
  <div class="left-content mr-16">
    <el-scrollbar ref="scrollRef" height="100%" class="scroll">
      <div class="flex mb-48" v-for="(item, index) in textList" :key="item.updateKey">
        <div class="user-avatar">
          <img v-if="item.resultTts || item.library" src="/img/avatar.png" alt="" />
          <img v-else src="/img/user_avatar.png" alt="" />
        </div>
        <div class="ml-12">
          <div class="time mb-11">
            <span v-if="item.resultTts || item.library">智能馆员{{ item.time }}</span>
            <span v-else>读者{{ item.time }}</span>
          </div>
          <div>
            <div
              class="answer"
              :class="item.resultTts || item.library ? 'libarary-bg' : 'user-color '"
            >
              <div v-if="item.resultTts || item.library">
                <van-loading v-if="!item.resultMessage" type="spinner" color="#1989fa" />
                <div v-if="item.isStop && item.stopText">{{ item.stopText }}</div>
                <div
                  v-if="item.resultTts && !item.isStop"
                  v-typewriter="item.resultTts"
                  :data-index="index"
                >
                  {{ item.typewriterText }}
                </div>
              </div>
              <div v-else>{{ item.resultMessage }}</div>
            </div>
            <div
              v-if="(!item.isStop && item.resultTts) || !item.resultMessage"
              class="stop-icon mt-18"
              @click="stop(item, index)"
            >
              停止生成
            </div>
          </div>
          <BookList
            v-if="item.dataList?.length && item.isStop"
            :data-list="item.dataList"
          ></BookList>
        </div>
      </div>
    </el-scrollbar>

  </div>
</template>

<script>
import { ref } from 'vue';
import { useEventBus } from '@/hooks/useEventBus';

const emits = defineEmits(['watchTypeWriter', 'handleStop']);

const textList = ref([]);
useEventBus('clearChatInfo', () => {
  textList.value = [];
});
useEventBus('changeAction', (message) => {
// message其它组件传递的数据 
  clearInterval(typeWriter.value);
  if (!textList.value.length || !message.resultTts) {
    textList.value.push({
      time: ` ${dayjs().format('HH:mm:ss')}`,
      isStop: false,  // 是否停止
      stopText: '',   // 打字机停止后的内容
      typewriterText: '',  // 动态展示打字机内容的文本
      updateKey: dayjs().valueOf(), // 每次增加一条数据的唯一key
      ...message
    });
    return;
  }

  if (textList.value.length && message.resultTts) {
    textList.value[textList.value.length - 1] = {
      ...textList.value[textList.value.length - 1],
      ...message
    };
  }
});

const stop = (item, index, isFinish = false) => {
  if (!item.resultMessage) {
    item.isStop = true;
    emits('handleStop');
    return;
  }
  if (item.isStop) {
    return;
  }

  const curText = document.querySelector(`[data-index="${index}"]`);
  item.isStop = true;
  item.stopText = curText?.innerText;
};
</script>

<style scoped lang="scss">
/* 自定义样式*/
</style>
相关推荐
茶茶只知道学习1 分钟前
通过鼠标移动来调整两个盒子的宽度(响应式)
前端·javascript·css
蒟蒻的贤4 分钟前
Web APIs 第二天
开发语言·前端·javascript
清灵xmf8 分钟前
揭开 Vue 3 中大量使用 ref 的隐藏危机
前端·javascript·vue.js·ref
su1ka11113 分钟前
re题(35)BUUCTF-[FlareOn4]IgniteMe
前端
测试界柠檬15 分钟前
面试真题 | web自动化关闭浏览器,quit()和close()的区别
前端·自动化测试·软件测试·功能测试·程序人生·面试·自动化
多多*16 分钟前
OJ在线评测系统 登录页面开发 前端后端联调实现全栈开发
linux·服务器·前端·ubuntu·docker·前端框架
2301_8010741516 分钟前
TypeScript异常处理
前端·javascript·typescript
ᅠᅠᅠ@17 分钟前
异常枚举;
开发语言·javascript·ecmascript
小阿飞_17 分钟前
报错合计-1
前端
caperxi19 分钟前
前端开发中的防抖与节流
前端·javascript·html