首页 前端知识 Vue3倒计时组件(Countdown)改写

Vue3倒计时组件(Countdown)改写

2024-04-20 17:04:05 前端知识 前端哥 197 701 我要收藏

首先 声明

这篇文章时基于 作者: theMuseCatcher 写的 Vue3倒计时组件(Countdown) 进行改写的.

因为在使用的过程中发现原作者的组件存在以下问题:
1.不能主动停止倒计时.
2.在倒计时countdown赋值为0的时候,还会在次执行一次渲染方法,导致出现00:0-1的情况
3.在countdown再次赋值后不能主动监听值变化进行渲染

所以我改写了部分代码逻辑实现了以上的问题

<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import type { Ref } from 'vue'
interface Props {
  countdown: number, // 倒计时数值(countdown),必传,支持设置未来某时刻的时间戳(ms) 或 相对剩余时间(s)
  title?: string, // 倒计时标题 string | v-slot
  format?: string, // 格式化倒计时展示,(Y:年,M:月,D:天,H:小时,m:分钟,s:秒)
  prefix?: string, // 倒计时数值的前缀 string | v-slot
  suffix?: string, // 倒计时数值的后缀 string | v-slot
  finishedText?: string // 完成后的展示文本 string | v-slot
  isStop?: boolean		// 是否停止倒计时
}
const props = withDefaults(defineProps<Props>(), {
  countdown: 0,
  title: 'Countdown',
  format: 'HH:mm:ss',
  prefix: '',
  suffix: '',
  finishedText: '',
  isStop: false
})
const restTime = ref(props.countdown)
let timer = ref()

let cd = toRefs(props).countdown;     // 倒计时重新赋值为0 结束倒计时
watch(cd, (newValue, oldValue) => {
  clearInterval(timer.value);
  restTime.value = newValue > Date.now() ? Math.floor((newValue - Date.now()) / 1000) : newValue;
  countDown();
});

const countDown = () => {
  timer.value = setInterval(() => {
    if (restTime.value < 1) { // js中Boolean(非0)都是true
      clearInterval(timer.value);
      emit('finish');
      return;
    }
    restTime.value--;
  },1000)
}

const showTime = computed(() => { // 展示的倒计时 监听restTime的每一次变化来达到重新渲染的目的
  return timeFormat(restTime.value)
})
const emit = defineEmits(['finish','stop'])

onMounted(() => {
  if (restTime.value > Date.now()) {
    restTime.value = Math.floor((restTime.value - Date.now()) / 1000)
  }
  countDown();
})

onUnmounted(() => {
  clearInterval(timer.value);
})

let stop = toRefs(props).isStop;     // 倒计时重新赋值为0 结束倒计时
watch(stop, (newValue, oldValue) => {
  if (newValue) {
    emit('stop')
    clearInterval(timer.value);
  } else {
    countDown()
  }
});

function fixedTwo (value: number): string {
  return value < 10 ? '0' + value : String(value)
}
function timeFormat (time: number): string {
  let showTime = props.format
  if (showTime.includes('s')) {
    var s = time
  } else {
    var s = 0
  }
  if (showTime.includes('m')) {
    s = s % 60 || 0
    var m = Math.floor((time - s) / 60)
  } else {
    var m = 0
  }
  if (showTime.includes('H')) {
    m = m % 60
    var H = Math.floor((time - s - m * 60) / 60 / 60)
  } else {
    var H = 0
  }
  if (showTime.includes('D')) {
    H = H % 24
    var D = Math.floor((time - s - m * 60 - H * 60 * 60) / 60 / 60 / 24)
  } else {
    var D = 0
  }
  if (showTime.includes('M')) {
    D = D % 30
    var M = Math.floor((time - s - m * 60 - H * 60 * 60 - D * 24 * 60 * 60) / 60 / 60 / 24 / 30)
  } else {
    var M = 0
  }
  if (showTime.includes('Y')) {
    M = M % 12
    var Y = Math.floor((time - s - m * 60 - H * 60 * 60 - D * 24 * 60 * 60 - M * 30 * 24 * 60 * 60) / 60 / 60 / 24 / 30 / 12)
  } else {
    var Y = 0
  }
  showTime = showTime.includes('ss') ? showTime.replace('ss', fixedTwo(s)) : showTime.replace('s', String(s))
  showTime = showTime.includes('mm') ? showTime.replace('mm', fixedTwo(m)) : showTime.replace('m', String(m))
  showTime = showTime.includes('HH') ? showTime.replace('HH', fixedTwo(H)) : showTime.replace('H', String(H))
  showTime = showTime.includes('DD') ? showTime.replace('DD', fixedTwo(D)) : showTime.replace('D', String(D))
  showTime = showTime.includes('MM') ? showTime.replace('MM', fixedTwo(M)) : showTime.replace('M', String(M))
  showTime = showTime.includes('YY') ? showTime.replace('YY', fixedTwo(Y)) : showTime.replace('Y', String(Y))
  return showTime
}

</script>
<template>
  <div class="m-countdown">
    <slot name="title">
      <p class="u-title">{{ props.title }}</p>
    </slot>
    <div class="u-time">
      <slot name="prefix" v-if="restTime > 0">{{ prefix }}</slot>
      <slot v-if="finishedText && restTime === 0" name="finish">{{ finishedText }}</slot>
      <span v-else>{{ showTime }}</span>
      <slot name="suffix" v-if="restTime > 0"> {{ suffix }}</slot>
    </div>
  </div>
</template>
<style lang="scss" scoped>
.m-countdown {
  display: inline-block;
  .u-title {
    margin-bottom: 4px;
    color: #00000073;
    font-size: 14px;
  }
  .u-time {
    color: #000000d9;
    font-size: 24px;
    line-height: 1.5;
  }
}
</style>

组件使用


<script setup lang="ts">
import Countdown from './Countdown.vue'
 
function onFinish () {
  console.log('countdown finished')
}
</script>
<template>
	<count-down
		:countdown="countDownTime"
	    :is-stop="isStop"
		format="m:ss"
		finishedText="倒计时结束"
		@finish="onFinish">
		<template #prefix>There's only </template>
	    <!-- <template #finish>&lt; FinishedText slot &gt;</template> -->
	    <template #suffix> left for the end.</template>
	</count-down>
</template>

如果要主动停止倒计时,对组件的props.isStop设置为true进行了,恢复计时设置为false

转载请注明出处或者链接地址:https://www.qianduange.cn//article/5479.html
标签
less
评论
发布的文章

CSS(8)空间转换 动画

2024-04-29 12:04:29

CSS介绍(4)--背景属性

2024-04-29 12:04:26

Web学习记录---CSS(1)

2024-04-29 12:04:17

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!