Vue组件实现卡片动画倒计时示例详解

作者:紫衣小生 时间:2024-04-29 13:08:00 

前言

最近有朋友在做投票的项目,里面有用到一个倒计时的组件,还想要个动画效果。cv * 浸染多年的我,首先想到的是直接找个现有的组件。

通过一通搜索,看上的只有一个 vue2-flip-countdown,但是当我要修改大小和颜色的时候发现改不了,从而直接把源码拉到项目里面,改起来也挺麻烦。

Vue组件实现卡片动画倒计时示例详解

而且,在搜索 * 运行几个周天以后,其实心理已经有了一个倒计时开发整体思路,便决定自己封装一个倒计时出来,最终实现效果如下:

Vue组件实现卡片动画倒计时示例详解

需求拆解

  • 开发一个开箱即用的组件,实现倒计时的效果,显示模式为 01日01时01分01秒。

  • 以终止时间为入参,计算倒计时显示的各个数据。

  • 方便的控制显示的文案,以及主题颜色,大小。

组件设计思路

  • 组件分为两个部分

    • animate-clock,控制组件数据结构,例如:自定义文案、将中文时间单位改成英文,是否换行等等

    • animate-card,动画卡片,即组件里面的具体数字部分,并加上动画。

  • clock 组件中,对传入的 props 进行处理,最核心的为 终止时间(terminalTime),根据终止时间计算天、时、分、秒。

  • 通过定时任务,把计算出来的 天、时、分、秒 数据传入 card 组件,触发视图更新。

  • card 组件中,当数据发生改变的时候触发动画效果。

  • 【升级】在此基础上进行扩展,变为一个倒计时通用解决方案:

    • 反向倒计时

    • 只有数字的倒计时

    • 只有分、秒的倒计时

    • 中文、英文字符串倒计时

    • 动画抽奖

    • 专注时间计时器

    • ...

具体开发

animate-clock.vue

首先设计视图部分:

<template>
 <div class="animate-clock">
   <!-- <p>{{days}}{{hours}}{{minites}}{{seconds}}</p> -->
   <span>距离结束还剩</span>
   <animate-card :val="days" :size="16" :self-disabled="disabled" />
   <span>天</span>
   <animate-card :val="hours" :size="16" :self-disabled="disabled" />
   <span>时</span>
   <animate-card :val="minites" :size="16" :self-disabled="disabled" />
   <span>分</span>
   <animate-card :val="seconds" :size="16" :self-disabled="disabled" />
   <span>秒</span>
 </div>
</template>
<style lang="scss" scoped>
.animate-clock {
 width: 100%;
 text-align: center;
 font-size: 16px;
 font-weight: bold;
 padding: 40px 0 ;
}
</style>

很简单的结构,现在版本为截图所示的一行结构,可以看到,完全可以通过业务组件中通过传入 props 的形式,修改每一个部分的文案,而且 样式也可以随时控制。

js 部分主要做这么几件事:

  • 接收 props,并声明 data

  • 声明一个 工具函数,用来 处理 小于 10 的数字,前面增加 0

  • 声明主要业务函数,被定时任务调用的更新数据方法

<script>
import animateCard from './animate-card.vue'
export default {
 components: { animateCard },
 props: {
   terminalTime: String,
 },
 data() {
   return {
     days: ['0', '0'],
     hours: ['0', '0'],
     minites: ['0', '0'],
     seconds: ['0', '0'],
     setIntVal: null,
     disabled: false,
   }
 },
 mounted() {
   // 先调用一次
   this.updateClock()
   // 箭头函数不修改当前作用域下的 this 指向
   this.setIntVal = setInterval(() => {
     this.updateClock()
   }, 1000)
 },
 methods: {
    /**
    * 更新计时器
    * @result void
    */
   updateClock() {
     let now = new Date().getTime()
     let stopTime = 0
     // 错误入参 处理逻辑
     try {
       stopTime = new Date(this.terminalTime).getTime()
     } catch (err) {
       console.error(err)
       return false
     }
     // 终止逻辑
     const remainingTime = stopTime - now
     if (remainingTime < 1000) {
       clearInterval(this.setIntVal)
       this.setIntVal = null
       // 计时器 清零
       this.days = this.hours = this.minites = this.seconds = ['0', '0']
       this.disabled = true
       console.log('时间到!')
       return false
     }
     // 计算 日、时、分、秒
     let days = parseInt(remainingTime / (24 * 60 * 60 * 1000))
     let hours = parseInt(
       (remainingTime - 24 * 60 * 60 * 1000 * days) / (60 * 60 * 1000)
     )
     let minites = parseInt(
       (remainingTime - 24 * 60 * 60 * 1000 * days - 60 * 60 * 1000 * hours) /
         (60 * 1000)
     )
     let seconds = parseInt(
       (remainingTime -
         24 * 60 * 60 * 1000 * days -
         60 * 60 * 1000 * hours -
         60 * 1000 * minites) /
         1000
     )
     // 更新 data
     this.days = this.toStringAndUnshiftZero(days)
     this.hours = this.toStringAndUnshiftZero(hours)
     this.minites = this.toStringAndUnshiftZero(minites)
     this.seconds = this.toStringAndUnshiftZero(seconds)
   },
   /**
    * 转化数字为数组,并在 头部填充 0
    * @params num: numnber
    * @result string[]
    */
   toStringAndUnshiftZero(num) {
     const val = num.toString().split('')
     if (num < 10) {
       val.unshift('0')
     }
     return val
   },
 },
}
</script>

这一块根本没有什么技术含量,主要是异常数据的处理,和停止逻辑。 其实计时器清零理论上是不需要出现的,但是在测试过程中发现,会出现最后一帧为 01 的情况,就直接清零了。

animate-card

这个组件一开始考虑的很复杂,想着监听数据的变化,触发一个动画的方法,然后这个方法支持重写,提高组件的扩展性,但是时间不允许,后面又觉得不太必要。

最后选择的方案很简单,却提供了一个可以实现 css 能实现的所有效果的思路。

主要思路是通过 vue 的 transition-group 机制,将 0-9 所有的卡片都渲染好,隐藏起来,通过 v-show 来触发绑定在 transition-group 上的动画效果,从而实现动态监听数据变化的效果。

需要注意的是因为宿主项目中 引入了 animate.css,所以就直接使用 animate 的动画效果了。

有需要的,可以翻看文档 【animate.css 官方文档】进行配置。

如果直接CV这套代码的话,没有动画效果。

代码如下:

<template>
 <div class="aimate-card">
   <div class="card-group" v-for="(item,idx) in val" :key="idx" :style="{'font-size': size+'px'}">
     <transition-group enter-active-class="animate__animated animate__bounceIn" leave-active-class="animate__animated animate__fadeOutDown">
       <div class="card-item" :class="{'disabled': selfDisabled}" v-for="num in 10" :key="num" v-show="item== num-1">{{num-1}}</div>
     </transition-group>
   </div>
 </div>
</template>
<script>
export default {
 props: {
   val: {
     type: Array,
     default: () => ['0', '0'],
   },
   size: {
     type: Number,
     default: 16,
   },
   selfDisabled: {
     type: Boolean,
     default: false,
   },
 },
 mounted() {
   console.log(this.selfDisabled)
 },
}
</script>
<style lang="scss" scoped>
.aimate-card {
 width: auto;
 display: inline-block;
 height: 100%;
 .card-group {
   display: inline-block;
   position: relative;
   width: 40px;
   padding: 5px;
   height: 100%;
   vertical-align: middle;
   .card-item {
     position: absolute;
     background: #3a7fe4;
     color: #fff;
     width: 30px;
     height: 40px;
     top: -20px;
     line-height: 40px;
   }
   .disabled {
     background: #ccc !important;
   }
 }
}
</style>

看完代码以后很容易发现,我设计的样式其实一点都不好看,可以对 card-item 写一些前端比较炫的效果。而且动画效果也可以自定义。

项目中使用

粘贴上面两个 vue 文件

在业务页面中 引入并使用

使用方法如下:

<div class="vote-clock">
   <animate-clock :terminalTime="'2023-07-11 23:27:00'" />
</div>

CV * 只支持 terminalTime 这一个入参。

后记

这个组件很简单,但是也有很多可以琢磨的地方,更重要的是整理出一个开发通用组件的思路。

另外,这套代码其实只是一个基础结构,扩展性还是很强的,尤其是前面设计思路中第5条中提到的其他功能,在此基础上可以很快速的进行开发,感兴趣的道友可以简单琢磨一下。

大家在工作中不可避免的会遇到多种多样的需要复用的代码块,有的道友会选择提出为一个公共组件或者公共代码块,有的道友则选择使用CV * 直接复用,也有的可能会另辟蹊径在第一个场景的基础上优化为第二套代码。

我以为这三种情况其实就是一个递进的关系,首先CV * 好,发功后发现场景不是完全一致,进行些许优化,进而搞出一个兼容多个可能存在的场景的通用解决方案。

在设计一个组件的时候,需要尽可能的考虑多种场景,并考虑一下后续的升级方案。想兼容所有的场景肯定不可能,那么就要知道当前组件的边界在哪里。所以,在设计组件的时候不能只着眼于当前业务,更要考虑到其他场景。最简单的例如:一个Button组件,除了保证全局的按钮风格一致的前提下,也要考虑到按钮禁用状态,大按钮,带图标按钮,按钮组这些方面的东西。

来源:https://juejin.cn/post/7122686532195991560

标签:Vue,卡片,动画,倒计时
0
投稿

猜你喜欢

  • javascript跨域原因以及解决方案分享

    2024-04-10 10:44:32
  • Python实现点阵字体读取与转换的方法

    2022-11-08 08:33:27
  • 详解js文件通过python访问数据库方法

    2024-01-20 06:09:09
  • python3+PyQt5实现自定义分数滑块部件

    2023-08-03 02:15:28
  • MySql在Mac上的安装与配置详解

    2024-01-27 03:09:52
  • VSCode 云同步扩展设置Settings Sync插件

    2022-08-30 03:20:54
  • 用ASP+CSS实现随机背景

    2007-09-26 12:33:00
  • PHP基于rabbitmq操作类的生产者和消费者功能示例

    2023-11-23 20:03:03
  • Django1.9 加载通过ImageField上传的图片方法

    2022-11-17 10:07:12
  • SQL Server 常用函数使用方法小结

    2024-01-13 11:39:32
  • 用 SA FileUp 上传多文件

    2008-07-04 13:44:00
  • Thinkphp5框架实现获取数据库数据到视图的方法

    2024-05-03 15:52:16
  • mysql错误处理之ERROR 1665 (HY000)

    2024-01-21 12:16:54
  • sqlserver bcp(数据导入导出工具)一般用法与命令详解

    2012-07-11 15:56:39
  • Django2.1.3 中间件使用详解

    2023-11-06 19:46:00
  • 浅谈JavaScript中的parseInt()的妙用

    2024-04-19 10:47:52
  • MySQL中的 Binlog 深度解析及使用详情

    2024-01-19 04:15:48
  • python中enumerate函数用法实例分析

    2023-04-02 05:14:20
  • Mysql 数据库双机热备的配置方法

    2010-06-09 19:13:00
  • Systemd 入门实战教程

    2022-10-29 15:52:44
  • asp之家 网络编程 m.aspxhome.com