使用 Vue 3 实现打字机效果

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

实现步骤

1. 创建自定义指令

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

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。该指令会在元素挂载时自动触发,逐字显示文本内容。

<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>

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值