Skip to content

打字机效果

🕒 Published at:
html
<script lang="ts" setup>
import { defineProps, ref, watch, withDefaults } from 'vue'

const { array, typeSpeed, delay, start, caret, iterations } = withDefaults(
  defineProps<{
    array: string[]
    typeSpeed: number
    delay: number
    start: number
    caret: string
    iterations: number
  }>(),
  {
    typeSpeed: 200,
    delay: 2000,
    start: 0,
    caret: 'cursor',
    iterations: 0
  }
)

const emit = defineEmits(['typed', 'typeStatus'])

const typeValue = ref('')
const count = ref(0)
const typeStatus = ref(false)
const arrayIndex = ref(0)
const charIndex = ref(0)

function typewriter() {
  let loop = 0
  if (charIndex.value < array[arrayIndex.value].length) {
    if (!typeStatus.value) {
      typeStatus.value = true
    }

    typeValue.value += array[arrayIndex.value].charAt(charIndex.value)
    charIndex.value += 1
    setTimeout(typewriter, typeSpeed)
  } else {
    count.value += 1

    onTyped(array[arrayIndex.value])

    if (count.value === array.length) {
      loop += 1
      if (loop === iterations) {
        return (typeStatus.value = false)
      }
    }

    typeStatus.value = false

    setTimeout(eraser, delay)
  }
}
function eraser() {
  // if (charIndex.value > 0) {
  //   if (!typeStatus.value) typeStatus.value = true
  //   typeValue.value = array[arrayIndex.value].substring(0, charIndex.value - 1)
  //   charIndex.value -= 1
  //   setTimeout(eraser, eraseSpeed)
  // } else {
  // typeStatus.value = false
  charIndex.value = 0
  typeValue.value = ''
  arrayIndex.value += 1
  if (arrayIndex.value >= array.length) arrayIndex.value = 0
  setTimeout(typewriter, 0)
  // }
}
function onTyped(typedString: string) {
  emit('typed', typedString)
}
setTimeout(typewriter, start)

watch(
  () => typeStatus.value,
  (val) => {
    emit('typeStatus', val)
  }
)
</script>

<template>
  <div class="is-typed">
    <slot></slot>
    <span class="typed">{{ typeValue }}</span>
    <span :class="caret + ' ' + { typing: typeStatus }">&nbsp;</span>
  </div>
</template>

<style lang="less" scoped>
.cursor {
  display: inline-block;
  width: 2px;
  margin: 0 2px;
  background: #333;
  border-radius: 2px;
  transform: scaleY(0.9);
}
</style>

调用

js
const subTitle = ref<string[]>([
  '第一句话',
  '第二话',
  '第三句话',
  '结束了'
])
<Writer :array="subTitle" :type-speed="70" :delay="5000"  />