我用Vue3+TS实现了一个新年倒计时组件,适用于各种场景

简介: 最近在写一个考试系统,有一个倒计时自动交卷的需求,正好也马上春节,就有了编写一个倒计时组件的想法。对于这个倒计时组件,它应该具有这样的功能:字体、颜色等样式可以由使用者自定义;
Hi~,我是 一碗周,一个在舒适区垂死挣扎的前端,如果写的文章有幸可以得到你的青睐,万分有幸~

写在前面

最近在写一个考试系统,有一个倒计时自动交卷的需求,正好也马上春节,就有了编写一个倒计时组件的想法。

对于这个倒计时组件,它应该具有这样的功能:

  • 字体、颜色等样式可以由使用者自定义;
  • 结束时间也可以由使用者自定义;
  • 倒计时结束以后,倒计时组件emit一个事件,以便进行后续操作。

现在我们根据这样的需求,去编写这个组件。

组件属性和事件

首先我们创建一个Vue3+TS+setup的基础组件,代码如下:

<template>
  <div></div>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue'
onMounted(() => {
  console.log('mounted!')
})
</script>
<style scoped></style>

然后定义组件的props和emits,方便后续编写,定义的内容如下:

interface Props {
  label?: string
  // 字体颜色和背景颜色,接收十六进制字符串
  textColor?: string
  backgroundColor?: string
  height?: string
  width?: string
  // 依次传递 label 时间 描述 的字体大小
  fontSize?: string[]
  mFontSize?: string[]
  // 结束时间,单位毫秒
  endTime?: number
}
const emit = defineEmits(['time-end'])
const props = withDefaults(defineProps<Props>(), {
  label: '春节倒计时',
  // 默认样式
  textColor: '#d99c3b',
  backgroundColor: '#cf1322',
  width: '100%',
  height: '100%',
  fontSize: () => {
    return ['2.2rem', '1.2rem', '3rem']
  },
  mFontSize: () => {
    return ['1.7rem', '0.8rem', '2rem']
  },
  // 2022年春节的时间戳,单位毫秒
  endTime: new Date(2022, 1, 1).getTime(),
})

这里我们使用了TS的类型约束,增加代码的健壮性。

静态效果

我们定义了内容之后,来编写一下静态效果,HTML骨架如下:

<div class="middle">
  <h1 class="label">{{ props.label }}</h1>
  <div class="time">
    <span>
      <div>20</div>
      天
    </span>
    <span>
      <div>12</div>
      时
    </span>
    <span>
      <div>20</div>
      分
    </span>
    <span>
      <div>40</div>
      秒
    </span>
  </div>
</div>

在Vue3中,我们可以在<style>标签中使用v-bind指令,具体可以参考单文件组件样式特性 | Vue.js (vuejs.org),我们的样式如下:

.middle {
  display: flex;
  flex-direction: column;
  justify-content: center;
  height: v-bind('props.height');
  width: v-bind('props.width');
  text-align: center;
  user-select: none;
  background-color: v-bind('props.backgroundColor');
}
.label {
  font-size: v-bind('props.fontSize[0]');
  color: v-bind('props.textColor');
}

.time {
  color: v-bind('props.textColor');
  text-transform: uppercase;
  display: flex;
  justify-content: center;
}

.time span {
  padding: 0 14px;
  font-size: v-bind('props.fontSize[1]');
}

.time span div {
  font-size: v-bind('props.fontSize[2]');
}

@media (max-width: 740px) {
  .label {
    font-size: v-bind('props.mFontSize[0]');
  }
  .time span {
    padding: 0 16px;
    font-size: v-bind('props.mFontSize[1]');
  }
  .time span div {
    font-size: v-bind('props.mFontSize[2]');
  }
}

它最终是下面这个样子:

image.png

获取剩余时间

现在我们有了基础的样式以及需要的属性,然后我们需要获取一下距离endTime的剩余时间,这里我们采用dayjs中提供的diff方法(因为我在项目中引入了day.js,如果没有的话,可以用两个时间戳做加法,效果是一样的),这个方法可以返回指定两个时间的差异,使用方法如下:

import dayjs from 'dayjs'
const timeLeft = ref(dayjs(props.endTime).diff(dayjs(Date.now()), 'seconds'))

返回endTime到现在的时间,单位是秒。

然后我们定义一个计算属性,来将这个秒格式化一下,示例代码如下:

const durationFormatter = computed(() => {
  const time = timeLeft.value
  if (!time) return { ss: 0 }
  let t = time
  const ss = t % 60
  t = (t - ss) / 60
  if (t < 1) return { ss }
  const mm = t % 60
  t = (t - mm) / 60
  if (t < 1) return { mm, ss }
  const hh = t % 24
  t = (t - hh) / 24
  if (t < 1) return { hh, mm, ss }
  const dd = t
  return { dd, hh, mm, ss }
})

获取之后我们只需要在<template>使用我们的计算属性,从而动态的显示数据。

开始倒计时

最后,我们还剩一步,就是让这个时间动起来。这里我们采用setTimeout而不是setInterVal,实现代码如下:

let timer: any = null
const getTime = () => {
  // 避免重复执行 setTimeout
  timer && clearTimeout(timer)
  timer = setTimeout(() => {
    if (timeLeft.value >= 0) {
      --timeLeft.value
      getTime() // 递归调用
    } else {
      emit('time-end', props.endTime)
    }
  }, 1000)
}

当倒计时结束后,将time-end事件emit到父组件,并将结束时间作为event参数。

使用组件

使用这个组件也比较简单,直接引入即可,代码如下:

<template>
  <countDown width="100vw" height="100vh" @time-end="timeEnd"></countDown>
</template>
<script setup lang="ts">
import { countDown } from '@/components/countDown/index'
const timeEnd = (e: any) => {
  console.log(e)
}
</script>
<style scoped></style>

运行效果如下:

倒计时组件.gif

写在最后

以上就是这篇文章的全部内容,如果觉着俺写的不错,可以点个赞支持下。

目录
相关文章
|
2月前
|
JavaScript
Vue中如何实现兄弟组件之间的通信
在Vue中,兄弟组件可通过父组件中转、事件总线、Vuex/Pinia或provide/inject实现通信。小型项目推荐父组件中转或事件总线,大型项目建议使用Pinia等状态管理工具,确保数据流清晰可控,避免内存泄漏。
300 2
|
1月前
|
JavaScript 前端开发 安全
Vue 3
Vue 3以组合式API、Proxy响应式系统和全面TypeScript支持,重构前端开发范式。性能优化与生态协同并进,兼顾易用性与工程化,引领Web开发迈向高效、可维护的新纪元。(238字)
493 139
|
1月前
|
缓存 JavaScript 算法
Vue 3性能优化
Vue 3 通过 Proxy 和编译优化提升性能,但仍需遵循最佳实践。合理使用 v-if、key、computed,避免深度监听,利用懒加载与虚拟列表,结合打包优化,方可充分发挥其性能优势。(239字)
204 1
|
2月前
|
开发工具 iOS开发 MacOS
基于Vite7.1+Vue3+Pinia3+ArcoDesign网页版webos后台模板
最新版研发vite7+vue3.5+pinia3+arco-design仿macos/windows风格网页版OS系统Vite-Vue3-WebOS。
365 11
|
1月前
|
JavaScript 安全
vue3使用ts传参教程
Vue 3结合TypeScript实现组件传参,提升类型安全与开发效率。涵盖Props、Emits、v-model双向绑定及useAttrs透传属性,建议明确声明类型,保障代码质量。
247 0
|
1月前
|
缓存 JavaScript
vue中的keep-alive问题(2)
vue中的keep-alive问题(2)
280 137
|
5月前
|
人工智能 JavaScript 算法
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
769 0
|
5月前
|
JavaScript UED
用组件懒加载优化Vue应用性能
用组件懒加载优化Vue应用性能
|
6月前
|
JavaScript 数据可视化 前端开发
基于 Vue 与 D3 的可拖拽拓扑图技术方案及应用案例解析
本文介绍了基于Vue和D3实现可拖拽拓扑图的技术方案与应用实例。通过Vue构建用户界面和交互逻辑,结合D3强大的数据可视化能力,实现了力导向布局、节点拖拽、交互事件等功能。文章详细讲解了数据模型设计、拖拽功能实现、组件封装及高级扩展(如节点类型定制、连接样式优化等),并提供了性能优化方案以应对大数据量场景。最终,展示了基础网络拓扑、实时更新拓扑等应用实例,为开发者提供了一套完整的实现思路和实践经验。
831 77
|
4月前
|
人工智能 JSON JavaScript
VTJ.PRO 首发 MasterGo 设计智能识别引擎,秒级生成 Vue 代码
VTJ.PRO发布「AI MasterGo设计稿识别引擎」,成为全球首个支持解析MasterGo原生JSON文件并自动生成Vue组件的AI工具。通过双引擎架构,实现设计到代码全流程自动化,效率提升300%,助力企业降本增效,引领“设计即生产”新时代。
403 1