vue中如何优雅的实现数字递增效果

简介: 【8月更文挑战第22天】vue中如何优雅的实现数字递增效果

需求

前几天项目开发中遇到一个需求,需要在数字更新时,实现其动态递增的效果
GIF 2022-12-11 15-51-08.gif
并且,在数字大于10000时,将单位进行转换,如数字大于10000时,单位从变为万
GIF 2022-12-11 15-54-49.gif
我们以vue中开发为例,一一实现下这两个需求。

实现

基本开发思路

实现数字递增,最容易的,我们应该可以想到通过setinterval函数实现数字累加功能。比如,我们的最终值是finanllyNum,我们可以很快想到这样的函数:

let finalNum = 1200
let count = 0
let timer = setInterval(() => {
   
   
  count ++ 
  if(count > finalNum){
   
   
    count = finalNum
    // 清空定时器
    clearInterval(timer)
    timer = null
  }
}, 10);

这个代码中,count是我们要实时在浏览器中显示的数字。我们每10ms实现数字进行一次递增,从而实现数字的动态递增特效。

指令的开发

由于项目中涉及很多这样的功能点,因此,这个功能应该被统一进行封装和复用。由于我们使用的是vue技术栈,显然,通过vue中的指令来实现这个功能最合适。

以下实现方案基于vue指令实现,因此,您需要参考官网了解学习vue中指令的基本用法。
vue2和vue3指令的用法大同小异,本示例以vue2进行演示

指令的基本结构

首先,我们创建change.js文件,写入指令的最基本结构

// js实现动态变化的数字
const directive = {
   
   
  inserted(el:any, binding:any) {
   
   
    ...
  },
};
export default directive

main.js中引入该文件

import Vue from "vue";
import App from "./App.vue";
import directive from "./change";

Vue.config.productionTip = false;
// 注册名为 change 的指令
Vue.directive("change", directive);
new Vue({
   
   
  render: (h) => h(App),
}).$mount("#app");

此时,我们创造了一个名为v-change的指令,我们直接使用

// App.vue
<template>
  <div id="app">
    <span> 240 </span>
  </div>
</template>

结合我们最开始的实现思路,可以完善指令

// js实现动态变化的数字
const directive = {
   
   
  inserted(el, binding) {
   
   
      // 获取到需要动态变化的数值
    let finalNum = el.innerText;
    let count = 0;
    let timer = setInterval(() => {
   
   
      count++;
      el.innerText = count;
      if (count > finalNum) {
   
   
        count = finalNum;
        el.innerText = count;
        // 清空定时器
        clearInterval(timer);
        timer = null;
      }
    }, 10);
  },
};
export default directive;

打开浏览器,可以看到我们的指令基本完成了。
GIF 2022-12-11 17-27-18.gif
但目前这个指令有个非常大的问题。我们设置的是每10ms增加一下数字(数字加1),如果数字非常大比如为12000,那么这个数字的动态变化过程岂不是非常长?
我们应该让数字无论多大,都在指定时间完成才行。

指令的优化

要想数字在指定时间完成变化,我们需要找到这个变化过程中的一些数学关系。目前,我们已经知道的有变化初始值0,变化最终值12000,动画总时长(假设为2S)。其次,每帧动画的间隔时长也是我们应该考虑的一个数值。

一开始的示例中,每10ms就是每帧动画的间隔时长。
简单理解,动画的间隔时长就是每秒动画变化的频率

我们通过两个图来理解一下:
如图,时间间隔为 166.7ms时,数字1s变化6次,分别为 100,200,300,400,500,600
image.png
当时间间隔为 100ms时,数字1s变化10次,以此为60,120,180,.......,600
image.png
可见,时间间隔越小,数字变化的也就越快。
现在我们可以得到这样一个数学关系:
动画总时长 / 时间间隔 * 数字的步长(每 1时间间隔增加的值) = 1200

现在,我们可以优化一下指令了

// js实现动态变化的数字
const directive = {
   
   
  inserted(el, binding) {
   
   
    // 最终要显示的数字
    let finalNum = el.innerText;
    // 动画间隔
    let rate = 30;
    // 总时长
    let time = 2000;
    // 步长,数据增加间隔
    let step = finalNum / (time / rate);
    // 初始值
    let count = 0;
    // 要保留的小数
    let timer = setInterval(() => {
   
   
      count = count + step;
      el.innerText = count.toFixed(0);
      if (count > finalNum) {
   
   
        count = finalNum;
        el.innerText = count;
        // 清空定时器
        clearInterval(timer);
        timer = null;
      }
    }, rate);
  },
};
export default directive;

效果非常不错!
GIF 2022-12-11 18-06-47.gif

实现单位变化

如何数字后面有单位,我们要怎么实现单位的变化呢?
第一种方法:
通过js动态为数字后赋值,这种方法很好理解

    let timer = setInterval(() => {
   
   
      count = count + step;
      if( count < 1000 ){
   
   
        el.innerText = count.toFixed(0) + "个"
      }else{
   
   
        el.innerText = (count / 1000 ).toFixed(0) + "千个"
      }
      if (count > finalNum) {
   
   
        count = finalNum;
        if( count < 1000 ){
   
   
          el.innerText = count.toFixed(0) + "个"
        }else{
   
   
          el.innerText = (count / 1000 ).toFixed(0) + "千个"
        }
        // 清空定时器
        clearInterval(timer);
        timer = null;
      }
    }, rate);

GIF 2022-12-11 18-16-03.gif
这个方法有一个很大的缺陷,就是单位必须写死在指令里,如下图,如果我们要实现很多动态数字而且需要不同单位该怎么办呢?

第二种方法:
指令是可以传递参数的

<template>
  <div id="app">
    <div >
      <span v-change="{eUnit:'个',eUnit:'万个'}"> 1200 </span>
    </div>
  </div>
</template>

通过binding.value可以接受参数。我们可以通过获取的参数,来处理逻辑,如下:

// js实现动态变化的数字
const directive = {
   
   
  inserted(el, binding) {
   
   
    // .....
    let timer = setInterval(() => {
   
   
      count = count + step;
      if( count < 1000 ){
   
   
        el.innerText = count.toFixed(0) +  binding.value.bUnit
      }else{
   
   
        el.innerText = (count / 1000 ).toFixed(0) + binding.value.eUnit
      }
      if (count > finalNum) {
   
   
        count = finalNum;
        if( count < 1000 ){
   
   
          el.innerText = count.toFixed(0) + binding.value.bUnit
        }else{
   
   
          el.innerText = (count / 1000 ).toFixed(0) + binding.value.eUnit
        }
        // 清空定时器
        clearInterval(timer);
        timer = null;
      }
    }, rate);
  },
};
export default directive;
相关文章
|
2天前
|
JavaScript 前端开发 开发者
Vue v-for 进阶指南:in 与 of 的区别及应用场景 | 笔记
Vue.js 中的 v-for 是强大的遍历指令,但其中的 in 和 of 关键字往往被开发者忽视。尽管它们的用法相似,但适用的场景和数据结构却各有不同。本文将详细探讨 v-for 中 in 和 of 的区别、适用场景以及在实际开发中的最佳使用时机。通过理解它们的差异,你将能够编写更加高效、简洁的 Vue.js 代码,灵活应对各种数据结构的遍历需求。
34 6
|
2天前
|
缓存 JavaScript 前端开发
Vue 中动态导入的注意事项
【10月更文挑战第12天】 在 Vue 项目中,动态导入是一种常用的按需加载模块的技术,可以提升应用性能和效率。本文详细探讨了动态导入的基本原理及注意事项,包括模块路径的正确性、依赖关系、加载时机、错误处理、缓存问题和兼容性等,并通过具体案例分析和解决方案,帮助开发者更好地应用动态导入技术。
|
2天前
|
JavaScript API
vue 批量自动引入并注册组件或路由等等
【10月更文挑战第12天】 vue 批量自动引入并注册组件或路由等等
|
2天前
|
JavaScript 算法 前端开发
深入剖析Vue中v-for的使用及index作为key的弊端
深入剖析Vue中v-for的使用及index作为key的弊端
9 2
|
4天前
|
监控 JavaScript 开发者
在 Vue 中,子组件为何不可以修改父组件传递的 Prop,如果修改了,Vue 是如何监控到属性的修改并给出警告的
在 Vue 中,子组件不能直接修改父组件传递的 Prop,以确保数据流的单向性和可预测性。如果子组件尝试修改 Prop,Vue 会通过响应式系统检测到这一变化,并在控制台发出警告,提示开发者避免这种操作。
|
2天前
|
JavaScript UED
Vue + ElementUI 实现动态添加和删除表单项的多层嵌套表单
【10月更文挑战第5天】本示例展示了如何在 Vue.js 中使用 Element UI 组件实现动态添加和删除嵌套表单项。该表单包含设备信息、设备部位及其对应的任务列表,支持用户动态添加设备部位和任务,并提供相应的表单验证规则。
16 0
Vue + ElementUI 实现动态添加和删除表单项的多层嵌套表单
|
2天前
|
JavaScript
《进阶篇第9章》学习vuex知识点后练习:求和案例_纯vue版代码
《进阶篇第9章》学习vuex知识点后练习:求和案例_纯vue版代码
8 1
|
2天前
|
JavaScript 前端开发 UED
vue中vue-router路由懒加载(按需加载)的作用以及常见的实现方法
vue中vue-router路由懒加载(按需加载)的作用以及常见的实现方法
22 1
|
3天前
|
JavaScript
vue 组件中的 data 为什么是一个函数 ?
【10月更文挑战第8天】 在 Vue 组件中,`data` 被定义为一个函数而非普通对象,以确保每个组件实例拥有独立的数据空间,避免数据混乱。这种方式还支持数据的响应式更新、组件的继承与扩展,并有助于避免潜在问题,提升应用的可靠性和性能。
11 2
|
3天前
|
JavaScript 开发者
Vue Render函数
【10月更文挑战第11天】 Vue 的 Render 函数提供了一种强大而灵活的方法来创建虚拟 DOM 节点,使开发者能够更精细地控制组件的构建过程。通过 `createElement` 参数,可以动态生成各种元素和组件,实现复杂逻辑和高级定制。尽管使用 Render 函数需要更多代码和对虚拟 DOM 的深入理解,但它在处理复杂场景时展现出巨大的优势。
8 2