我用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

写在最后

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

目录
相关文章
|
24天前
|
缓存 JavaScript UED
Vue3中v-model在处理自定义组件双向数据绑定时有哪些注意事项?
在使用`v-model`处理自定义组件双向数据绑定时,要仔细考虑各种因素,确保数据的准确传递和更新,同时提供良好的用户体验和代码可维护性。通过合理的设计和注意事项的遵循,能够更好地发挥`v-model`的优势,实现高效的双向数据绑定效果。
126 64
|
24天前
|
前端开发 JavaScript 测试技术
Vue3中v-model在处理自定义组件双向数据绑定时,如何避免循环引用?
Web 组件化是一种有效的开发方法,可以提高项目的质量、效率和可维护性。在实际项目中,要结合项目的具体情况,合理应用 Web 组件化的理念和技术,实现项目的成功实施和交付。通过不断地探索和实践,将 Web 组件化的优势充分发挥出来,为前端开发领域的发展做出贡献。
29 8
|
23天前
|
存储 JavaScript 数据管理
除了provide/inject,Vue3中还有哪些方式可以避免v-model的循环引用?
需要注意的是,在实际开发中,应根据具体的项目需求和组件结构来选择合适的方式来避免`v-model`的循环引用。同时,要综合考虑代码的可读性、可维护性和性能等因素,以确保系统的稳定和高效运行。
23 1
|
23天前
|
JavaScript
Vue3中使用provide/inject来避免v-model的循环引用
`provide`和`inject`是 Vue 3 中非常有用的特性,在处理一些复杂的组件间通信问题时,可以提供一种灵活的解决方案。通过合理使用它们,可以帮助我们更好地避免`v-model`的循环引用问题,提高代码的质量和可维护性。
33 1
|
27天前
|
JavaScript API 开发者
Vue是如何进行组件化的
Vue是如何进行组件化的
|
29天前
|
JavaScript 前端开发 开发者
vue 数据驱动视图
总之,Vue 数据驱动视图是一种先进的理念和技术,它为前端开发带来了巨大的便利和优势。通过理解和应用这一特性,开发者能够构建出更加动态、高效、用户体验良好的前端应用。在不断发展的前端领域中,数据驱动视图将继续发挥重要作用,推动着应用界面的不断创新和进化。
|
3天前
|
JavaScript 关系型数据库 MySQL
基于VUE的校园二手交易平台系统设计与实现毕业设计论文模板
基于Vue的校园二手交易平台是一款专为校园用户设计的在线交易系统,提供简洁高效、安全可靠的二手商品买卖环境。平台利用Vue框架的响应式数据绑定和组件化特性,实现用户友好的界面,方便商品浏览、发布与管理。该系统采用Node.js、MySQL及B/S架构,确保稳定性和多功能模块设计,涵盖管理员和用户功能模块,促进物品循环使用,降低开销,提升环保意识,助力绿色校园文化建设。
|
1月前
|
JavaScript 前端开发 开发者
vue学习第一章
欢迎来到我的博客!我是瑞雨溪,一名热爱前端的大一学生,专注于JavaScript与Vue,正向全栈进发。博客分享Vue学习心得、命令式与声明式编程对比、列表展示及计数器案例等。关注我,持续更新中!🎉🎉🎉
36 1
vue学习第一章
|
1月前
|
JavaScript 前端开发 索引
vue学习第三章
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中的v-bind指令,包括基本使用、动态绑定class及style等,希望能为你的前端学习之路提供帮助。持续关注,更多精彩内容即将呈现!🎉🎉🎉
26 1
vue学习第三章
|
1月前
|
缓存 JavaScript 前端开发
vue学习第四章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中计算属性的基本与复杂使用、setter/getter、与methods的对比及与侦听器的总结。如果你觉得有用,请关注我,将持续更新更多优质内容!🎉🎉🎉
35 1
vue学习第四章
下一篇
DataWorks