商城类应用开发中经常要遇到秒杀价或者到时间点开始优惠,这种业务了逻辑通常需要使用到倒计时功能。
主要使用到setTimeout方法,循环的不断调用清除调用清除,具体代码实现
import { cancelRaf, rAF } from '@/utils/raf' import { ref, computed, type Ref } from 'vue' //定义一个时间类型 type currentTime = { days: number hours: number minutes: number seconds: number millsecond: number total: number } type UseCountDownOptions = { time: number millisecond?: boolean onchenge?: (current: currentTime) => void finish?: () => void } const SECOND = 1000 const MINUTE = SECOND * 60 const HOURS = 60 * MINUTE const DAY = 24 * HOURS const parseTime = (time: number) => { const days = Math.floor(time / DAY) const hours = Math.floor((time % DAY) / HOURS) const minutes = Math.floor((time % HOURS) / MINUTE) const seconds = Math.floor((time % MINUTE) / SECOND) const millsecond = Math.floor(time % SECOND) return { days, hours, minutes, seconds, millsecond, total: time, } } const isSameSecond = (time1: number, time2: number) => { return Math.floor(time1 / SECOND) === Math.floor(time2 / SECOND) } export function UseCountDown(optoin: UseCountDownOptions) { let refId: number let countting: boolean let endTme: number //倒计时剩余时间 const remain: Ref = ref(optoin.time) const current = computed(() => parseTime(remain.value)) const pause = () => { countting = false optoin.finish?.() } const getCurrentRemain = () => Math.max(endTme - Date.now(), 0) const resetRemain = (value: number) => { remain.value = Math.max(endTme - Date.now(), 0) optoin.onchenge?.(current.value) if (value === 0) { pause() cancelRaf(refId) } } //毫秒级倒计时循环 const miroTick = () => { refId = rAF(() => { if (countting) { resetRemain(getCurrentRemain()) if (remain.value > 0) { miroTick() } } }) } //秒级倒计时循环 const maroTick = () => { refId = rAF(() => { if (countting) { const cRemain = getCurrentRemain() if (!isSameSecond(cRemain, remain.value) || cRemain === 0) { resetRemain(cRemain) } if (remain.value > 0) { maroTick() } } }) } const start = () => { if (!countting) { countting = true endTme = optoin.time + Date.now() if (optoin.millisecond) { miroTick() } else { maroTick() } } } const reset = (totalTime = optoin.time) => { pause() remain.value = totalTime } return { start, pause, reset, current, } }
上面的rAf实现如下
export const rAF = requestAnimationFrame || function (callback) { setTimeout(callback, 1000 / 60) } // requestAnimationFrame 屏幕刷新函数,每秒钟刷新60次,版本太低的浏览器没有这个函数,使用 1000 / 60 timout 模拟 export const cancelRaf = cancelAnimationFrame || function (id: number) { clearTimeout(id) } export const doubleRaf = (fn: () => void) => { rAF(() => { rAF(fn) }) }
使用举例
<script setup lang="ts"> import type { ICountdown } from '@/types' import { UseCountDown } from '@/use/useCountDown' interface IProps { data: ICountdown } const props = defineProps<IProps>() const countDown = UseCountDown({ time: props.data.time }) // 开始计时 countDown.start() const current = countDown.current const padStart = (num: number) => { return num.toString().padStart(2, '0') } </script> <template> <div class="home-countdown"> <div class="home-countdown__info"> <span class="number">{{ padStart(current.hours) }}</span> <span class="colon">:</span> <span class="number">{{ padStart(current.minutes) }}</span> <span class="colon">:</span> <span class="number">{{ padStart(current.seconds) }}</span> </div> </div> </template> .home-countdown { border-radius: 8px; width: 180px; height: 180px; background: linear-gradient(to bottom, rgb(252, 202, 202), white, white, white); padding: 15px 10px; box-sizing: border-box; justify-content: end; &__info { margin: 0 auto; text-align: center; display: flex; align-items: center; .number { font-size: 12px; background: rgb(252, 78, 78); color: white; padding: 2px; border-radius: 2px; width: 20px; font-weight: bold; } .colon { margin: 0 1px; color: red; } } }
具体代码地址:http://github.com/duzhaoquan/ele-h5.git
标签:current,Vue,const,小组,number,value,倒计时,time,Math From: https://www.cnblogs.com/duzhaoquan/p/17934805.html