首页 > 其他分享 >Vue 倒计时小组件

Vue 倒计时小组件

时间:2023-12-29 14:35:09浏览次数:37  
标签:current Vue const 小组 number value 倒计时 time Math

商城类应用开发中经常要遇到秒杀价或者到时间点开始优惠,这种业务了逻辑通常需要使用到倒计时功能。

 主要使用到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

相关文章

  • vue3引入使用svg图标
    vue3使用svg图标安装//通过命令安装2个插件npmivite-plugin-svg-icons-Dnpmifast-glob-D在vue.config.js中配置//vue.config.jsimport{fileURLToPath,URL}from'node:url'import{defineConfig}from'vite'importvuefrom'@vitejs/plugin-v......
  • 摸鱼摸出来的vue3+element-plus毒蘑菇后台管理:新标签页的实现。
    在浏览器中,点击标签页右边的加号可以新加一个标签页,所以,在毒蘑菇后台管理(简称毒蘑菇儿)中也可以这样操作。点击标签页右边的+按钮就可以打开一个新标签页了,可以打开多个,互不冲突,在新标签页中可以搜索你想要打开的页面,点击后会将该标签页替换成你点击后的页面(跟浏览器操作一致)。点......
  • vue 将百度地图或者高德地图组件化
    一、前言百度地图已经有了react相关的组件库,本人用的百度地图v3.0和vue3我仅仅是抛砖引玉,百度地图webgl、高德地图都是一样的,因为底层都是通过js控制地图如果用组件的方式开发,比如我将BMap.Marker作为一个组件,我暴露一个参数position,其目的是控制BMap.Marker位置......
  • vue3+ts打开echarts的正确方式
    实例项目使用vite5+vue3+ts,项目地址vite-vue3-charts,预览地址https://weizwz.com/vite-vue3-charts准备工作1.注册为百度地图开发者官网地址,然后在应用管理->我的应用里,创建应用,创建好后复制AK2.在根目录的index.html里引入百度地图<head><metacharse......
  • vue中get和post请求
    vue中和后台交互,首先要引用vue-resource.jsvue-resource.js是专门和后台进行交互<!--==============引入vue-resource插件=================--><scriptsrc="../js/vueJs/vue-resource.js"></script>vue中get请求functiongetRequest(url,params){returnnewPromis......
  • vue3+lottie实现动画
    1、安装lottie-webnpmilottie-web2、使用在线json文件<template><divclass="box"><divid="lottie_box"style="width:800px;height:800px;margin-left:1000px;background-color:pink"></div><butt......
  • Vue中$router.push()路由切换、如何传参和获取参数 和获取不到$router.push 参数问题
    路由的两种传参方式:  一:声明式<router-link:to="{path:'/login'}">Home</router-link> 二:编程式$router.push(...) //该方法的参数可以是一个字符串路径,或者一个描述地址的对象。不带参数写法://字符串(对应填写上面的path) this.$router.push('/login') //对......
  • 封装一个表情包组件(支持自定义表情图片)(基于vue3语法)
    效果图文件图直接贴代码emotion.vue<template><divclass="emotion-containerbeauty-scroll-livechat"><divclass="emotion-btn"@click="toggleEmotionShow"><spanclass="iconfonticon-biaoqing1&quo......
  • vuejs+echarts实现时间轴
    1、效果图2、具体需求描述①可以设置时间轴起始值;②时间轴可以缩放、左右拖动;③鼠标移入时间轴显示当前刻度信息;④点击时间轴时添加蓝色图标,鼠标移入图标显示此时图标信息且隐藏刻度信息,按住图标可以拖动图标;3、实现①结构代码<divid="timeAxisEchart"style="width:10......
  • vuejs+echarts实现x轴为时间轴且数据区域可缩放
    1、效果图2、具体功能描述①选择的时间为时间轴的开始和结束时间;②鼠标可以左右拖动x轴时间轴;③鼠标放入图表中可以缩放数据,x轴会相应的更改当前坐标轴间隔值,最小间隔值为1分钟,最大间隔值为1年,且在缩放时可以获取到数据窗口范围的起始数值;④点击y轴名称会对相应数据显示隐......