| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- import { ref, computed, onBeforeUnmount } from 'vue'
- import { isDef } from '../common/util'
- import { useRaf } from './useRaf'
- // 定义倒计时时间的数据结构
- export type CurrentTime = {
- days: number
- hours: number
- total: number
- minutes: number
- seconds: number
- milliseconds: number
- }
- // 定义倒计时的配置项
- export type UseCountDownOptions = {
- time: number // 倒计时总时间,单位为毫秒
- millisecond?: boolean // 是否开启毫秒级倒计时,默认为 false
- onChange?: (current: CurrentTime) => void // 倒计时每次变化时的回调函数
- onFinish?: () => void // 倒计时结束时的回调函数
- }
- // 定义常量
- const SECOND = 1000
- const MINUTE = 60 * SECOND
- const HOUR = 60 * MINUTE
- const DAY = 24 * HOUR
- // 将时间转换为倒计时数据结构
- function parseTime(time: number): CurrentTime {
- const days = Math.floor(time / DAY)
- const hours = Math.floor((time % DAY) / HOUR)
- const minutes = Math.floor((time % HOUR) / MINUTE)
- const seconds = Math.floor((time % MINUTE) / SECOND)
- const milliseconds = Math.floor(time % SECOND)
- return {
- total: time,
- days,
- hours,
- minutes,
- seconds,
- milliseconds
- }
- }
- // 判断两个时间是否在同一秒内
- function isSameSecond(time1: number, time2: number): boolean {
- return Math.floor(time1 / 1000) === Math.floor(time2 / 1000)
- }
- // 定义 useCountDown 函数
- export function useCountDown(options: UseCountDownOptions) {
- let endTime: number // 结束时间
- let counting: boolean // 是否计时中
- const { start: startRaf, cancel: cancelRaf } = useRaf(tick)
- const remain = ref(options.time) // 剩余时间
- const current = computed(() => parseTime(remain.value)) // 当前倒计时数据
- // 暂停倒计时
- const pause = () => {
- counting = false
- cancelRaf()
- }
- // 获取当前剩余时间
- const getCurrentRemain = () => Math.max(endTime - Date.now(), 0)
- // 设置剩余时间
- const setRemain = (value: number) => {
- remain.value = value
- isDef(options.onChange) && options.onChange(current.value)
- if (value === 0) {
- pause()
- isDef(options.onFinish) && options.onFinish()
- }
- }
- // 每毫秒更新一次倒计时
- const microTick = () => {
- if (counting) {
- setRemain(getCurrentRemain())
- if (remain.value > 0) {
- startRaf()
- }
- }
- }
- // 每秒更新一次倒计时
- const macroTick = () => {
- if (counting) {
- const remainRemain = getCurrentRemain()
- if (!isSameSecond(remainRemain, remain.value) || remainRemain === 0) {
- setRemain(remainRemain)
- }
- if (remain.value > 0) {
- startRaf()
- }
- }
- }
- // 根据配置项选择更新方式
- function tick() {
- if (options.millisecond) {
- microTick()
- } else {
- macroTick()
- }
- }
- // 开始倒计时
- const start = () => {
- if (!counting) {
- endTime = Date.now() + remain.value
- counting = true
- startRaf()
- }
- }
- // 重置倒计时
- const reset = (totalTime: number = options.time) => {
- pause()
- remain.value = totalTime
- }
- // 在组件卸载前暂停倒计时
- onBeforeUnmount(pause)
- return {
- start,
- pause,
- reset,
- current
- }
- }
|