前言

vue3不支持vue-count-to插件,无法使用vue-count-to实现数字动效,数字自动分割,vue-count-to主要针对vue2使用,vue3按照会报错:
TypeError: Cannot read properties of undefined (reading '_c')
的错误信息。这个时候我们只能自己封装一个CountTo组件实现数字动效。先来看效果图:

思路

使用Vue.component定义公共组件,使用window.requestAnimationFrame(首选,次选setTimeout)来循环数字动画,window.cancelAnimationFrame取消数字动画效果,封装一个requestAnimationFrame.js公共文件,CountTo.vue组件,入口导出文件index.js。

文件目录

使用示例

<CountTo:start="0" // 从数字多少开始:end="endCount" // 到数字多少结束:autoPlay="true" // 自动播放:duration="3000" // 过渡时间prefix="¥"   // 前缀符号suffix="rmb" // 后缀符号/>

入口文件index.js


const UILib = {install(Vue) {Vue.component('CountTo', CountTo)}
}export default UILib

main.js使用

import CountTo from './components/count-to/index';
app.use(CountTo)

requestAnimationFrame.js思路

  1. 先判断是不是浏览器还是其他环境
  2. 如果是浏览器判断浏览器内核类型
  3. 如果浏览器不支持requestAnimationFrame,cancelAnimationFrame方法,改写setTimeout定时器
  4. 导出两个方法 requestAnimationFrame, cancelAnimationFrame
各个浏览器前缀:let prefixes =  'webkit moz ms o';
判断是不是浏览器:let isServe = typeof window == 'undefined';
增加各个浏览器前缀:
let prefix;
let requestAnimationFrame;
let cancelAnimationFrame;
// 通过遍历各浏览器前缀,来得到requestAnimationFrame和cancelAnimationFrame在当前浏览器的实现形式for (let i = 0; i < prefixes.length; i++) {if (requestAnimationFrame && cancelAnimationFrame) { break }prefix = prefixes[i]requestAnimationFrame = requestAnimationFrame || window[prefix + 'RequestAnimationFrame']cancelAnimationFrame = cancelAnimationFrame || window[prefix + 'CancelAnimationFrame'] || window[prefix + 'CancelRequestAnimationFrame']}//不支持使用setTimeout方式替换:模拟60帧的效果// 如果当前浏览器不支持requestAnimationFrame和cancelAnimationFrame,则会退到setTimeoutif (!requestAnimationFrame || !cancelAnimationFrame) {requestAnimationFrame = function (callback) {const currTime = new Date().getTime()// 为了使setTimteout的尽可能的接近每秒60帧的效果const timeToCall = Math.max(0, 16 - (currTime - lastTime))const id = window.setTimeout(() => {callback(currTime + timeToCall)}, timeToCall)lastTime = currTime + timeToCallreturn id}cancelAnimationFrame = function (id) {window.clearTimeout(id)}}

完整代码:

requestAnimationFrame.js

let lastTime = 0
const prefixes = 'webkit moz ms o'.split(' ') // 各浏览器前缀let requestAnimationFrame
let cancelAnimationFrame// 判断是否是服务器环境
const isServer = typeof window === 'undefined'
if (isServer) {requestAnimationFrame = function () {return}cancelAnimationFrame = function () {return}
} else {requestAnimationFrame = window.requestAnimationFramecancelAnimationFrame = window.cancelAnimationFramelet prefix// 通过遍历各浏览器前缀,来得到requestAnimationFrame和cancelAnimationFrame在当前浏览器的实现形式for (let i = 0; i < prefixes.length; i++) {if (requestAnimationFrame && cancelAnimationFrame) { break }prefix = prefixes[i]requestAnimationFrame = requestAnimationFrame || window[prefix + 'RequestAnimationFrame']cancelAnimationFrame = cancelAnimationFrame || window[prefix + 'CancelAnimationFrame'] || window[prefix + 'CancelRequestAnimationFrame']}// 如果当前浏览器不支持requestAnimationFrame和cancelAnimationFrame,则会退到setTimeoutif (!requestAnimationFrame || !cancelAnimationFrame) {requestAnimationFrame = function (callback) {const currTime = new Date().getTime()// 为了使setTimteout的尽可能的接近每秒60帧的效果const timeToCall = Math.max(0, 16 - (currTime - lastTime))const id = window.setTimeout(() => {callback(currTime + timeToCall)}, timeToCall)lastTime = currTime + timeToCallreturn id}cancelAnimationFrame = function (id) {window.clearTimeout(id)}}
}export { requestAnimationFrame, cancelAnimationFrame }

CountTo.vue组件思路

首先引入requestAnimationFrame.js,使用requestAnimationFrame方法接受count函数,还需要格式化数字,进行正则表达式转换,返回我们想要的数据格式。

引入 import { requestAnimationFrame, cancelAnimationFrame } from './requestAnimationFrame.js'

需要接受的参数:

const props = defineProps({start: {type: Number,required: false,default: 0},end: {type: Number,required: false,default: 0},duration: {type: Number,required: false,default: 5000},autoPlay: {type: Boolean,required: false,default: true},decimals: {type: Number,required: false,default: 0,validator (value) {return value >= 0}},decimal: {type: String,required: false,default: '.'},separator: {type: String,required: false,default: ','},prefix: {type: String,required: false,default: ''},suffix: {type: String,required: false,default: ''},useEasing: {type: Boolean,required: false,default: true},easingFn: {type: Function,default(t, b, c, d) {return c * (-Math.pow(2, -10 * t / d) + 1) * 1024 / 1023 + b;}}
})

启动数字动效

const startCount = () => {state.localStart = props.startstate.startTime = nullstate.localDuration = props.durationstate.paused = falsestate.rAF = requestAnimationFrame(count)
}

核心函数,对数字进行转动

  if (!state.startTime) state.startTime = timestampstate.timestamp = timestampconst progress = timestamp - state.startTimestate.remaining = state.localDuration - progress// 是否使用速度变化曲线if (props.useEasing) {if (stopCount.value) {state.printVal = state.localStart - props.easingFn(progress, 0, state.localStart - props.end, state.localDuration)} else {state.printVal = props.easingFn(progress, state.localStart, props.end - state.localStart, state.localDuration)}} else {if (stopCount.value) {state.printVal = state.localStart - ((state.localStart - props.end) * (progress / state.localDuration))} else {state.printVal = state.localStart + (props.end - state.localStart) * (progress / state.localDuration)}}if (stopCount.value) {state.printVal = state.printVal < props.end ? props.end : state.printVal} else {state.printVal = state.printVal > props.end ? props.end : state.printVal}state.displayValue = formatNumber(state.printVal)if (progress < state.localDuration) {state.rAF = requestAnimationFrame(count)} else {emits('callback')}
}// 格式化数据,返回想要展示的数据格式
const formatNumber = (val) => {val = val.toFixed(props.default)val += ''const x = val.split('.')let x1 = x[0]const x2 = x.length > 1 ? props.decimal + x[1] : ''const rgx = /(\d+)(\d{3})/if (props.separator && !isNumber(props.separator)) {while (rgx.test(x1)) {x1 = x1.replace(rgx, '$1' + props.separator + '$2')}}return props.prefix + x1 + x2 + props.suffix
}

取消动效

// 组件销毁时取消动画
onUnmounted(() => {cancelAnimationFrame(state.rAF)
})

完整代码

<template>{{ state.displayValue }}
</template><script setup>  // vue3.2新的语法糖, 编写代码更加简洁高效
import { onMounted, onUnmounted, reactive } from "@vue/runtime-core";
import { watch, computed } from 'vue';
import { requestAnimationFrame, cancelAnimationFrame } from './requestAnimationFrame.js'
// 定义父组件传递的参数
const props = defineProps({start: {type: Number,required: false,default: 0},end: {type: Number,required: false,default: 0},duration: {type: Number,required: false,default: 5000},autoPlay: {type: Boolean,required: false,default: true},decimals: {type: Number,required: false,default: 0,validator (value) {return value >= 0}},decimal: {type: String,required: false,default: '.'},separator: {type: String,required: false,default: ','},prefix: {type: String,required: false,default: ''},suffix: {type: String,required: false,default: ''},useEasing: {type: Boolean,required: false,default: true},easingFn: {type: Function,default(t, b, c, d) {return c * (-Math.pow(2, -10 * t / d) + 1) * 1024 / 1023 + b;}}
})const isNumber = (val) => {return !isNaN(parseFloat(val))
}// 格式化数据,返回想要展示的数据格式
const formatNumber = (val) => {val = val.toFixed(props.default)val += ''const x = val.split('.')let x1 = x[0]const x2 = x.length > 1 ? props.decimal + x[1] : ''const rgx = /(\d+)(\d{3})/if (props.separator && !isNumber(props.separator)) {while (rgx.test(x1)) {x1 = x1.replace(rgx, '$1' + props.separator + '$2')}}return props.prefix + x1 + x2 + props.suffix
}// 相当于vue2中的data中所定义的变量部分
const state = reactive({localStart: props.start,displayValue: formatNumber(props.start),printVal: null,paused: false,localDuration: props.duration,startTime: null,timestamp: null,remaining: null,rAF: null
})// 定义一个计算属性,当开始数字大于结束数字时返回true
const stopCount = computed(() => {return props.start > props.end
})
// 定义父组件的自定义事件,子组件以触发父组件的自定义事件
const emits = defineEmits(['onMountedcallback', 'callback'])const startCount = () => {state.localStart = props.startstate.startTime = nullstate.localDuration = props.durationstate.paused = falsestate.rAF = requestAnimationFrame(count)
}watch(() => props.start, () => {if (props.autoPlay) {startCount()}
})watch(() => props.end, () => {if (props.autoPlay) {startCount()}
})
// dom挂在完成后执行一些操作
onMounted(() => {if (props.autoPlay) {startCount()}emits('onMountedcallback')
})
// 暂停计数
const pause = () => {cancelAnimationFrame(state.rAF)
}
// 恢复计数
const resume = () => {state.startTime = nullstate.localDuration = +state.remainingstate.localStart = +state.printValrequestAnimationFrame(count)
}const pauseResume = () => {if (state.paused) {resume()state.paused = false} else {pause()state.paused = true}
}const reset = () => {state.startTime = nullcancelAnimationFrame(state.rAF)state.displayValue = formatNumber(props.start)
}const count = (timestamp) => {if (!state.startTime) state.startTime = timestampstate.timestamp = timestampconst progress = timestamp - state.startTimestate.remaining = state.localDuration - progress// 是否使用速度变化曲线if (props.useEasing) {if (stopCount.value) {state.printVal = state.localStart - props.easingFn(progress, 0, state.localStart - props.end, state.localDuration)} else {state.printVal = props.easingFn(progress, state.localStart, props.end - state.localStart, state.localDuration)}} else {if (stopCount.value) {state.printVal = state.localStart - ((state.localStart - props.end) * (progress / state.localDuration))} else {state.printVal = state.localStart + (props.end - state.localStart) * (progress / state.localDuration)}}if (stopCount.value) {state.printVal = state.printVal < props.end ? props.end : state.printVal} else {state.printVal = state.printVal > props.end ? props.end : state.printVal}state.displayValue = formatNumber(state.printVal)if (progress < state.localDuration) {state.rAF = requestAnimationFrame(count)} else {emits('callback')}
}
// 组件销毁时取消动画
onUnmounted(() => {cancelAnimationFrame(state.rAF)
})
</script>

总结

自己封装数字动态效果需要注意各个浏览器直接的差异,手动pollyfill,暴露出去的props参数需要有默认值,数据的格式化可以才有正则表达式的方式,组件的驱动必须是数据变化,根据数据来驱动页面渲染,防止页面出现卡顿,不要强行操作dom,引入的组件可以全局配置,后续组件可以服用,码字不易,请各位看官大佬多多支持,一键三连了~❤️❤️❤️

demo演示

后续的线上demo演示会放在
demo演示
完整代码会放在
个人主页

希望对vue开发者有所帮助~

  1. 个人简介:承吾
  2. 工作年限:5年前端
  3. 地区:上海
  4. 个人宣言:立志出好文,传播我所会的,有好东西就及时与大家共享!

vue3 | 数据可视化实现数字滚动特效相关推荐

  1. vue大数据可视化【数字滚动效果】

    大数据可视化(基于echarts的一些小组件) 数据滚动效果 ![在这里插入图片描述](https://img-blog.csdnimg.cn/cb94b075f2514ddfb585924e6ff5 ...

  2. css3数字滚动特效简单实现

    放在大屏里面的数字滚动特效的简单实现, 思路参考:https://blog.csdn.net/nidunlove/article/details/78257549 结合业务需求和自己的思考拿vue写了 ...

  3. jquery实现数字滚动特效

    一.引入js文件 <script src="https://code.jquery.com/jquery-3.3.1.js"></script> <s ...

  4. html数字滚动动画效果,高效的jquery数字滚动特效

    本文实例讲述了基于jquery数字滚动特效的代码,分为四种情况分享给大家供大家参考,具体如下: 有分隔符,有小数点: 只有分隔符: 只有小数点: 无分隔符,无小数点: 运行效果图: 具体代码如下 数字 ...

  5. jquery 数字滚动特效 数字自增特效 数字位数动态适应

    最近做了个大项目,需要在首页动画显示实时统计数据,虽然百度了不少jquery特效,但有的需要积分,有的功能不全面,下面我将源码分享出来. html <!DOCTYPE html> < ...

  6. h5 数字变化_那些H5用到的技术(6)——数字滚动特效

    前言 会有这么一种情况,H5页面需要进行数字统计展示,以此来强调产品or工作的成果.如果只是静态显示一个数字,总是感觉生硬.对比如下: 是不是瞬间高大上了呢? 这个效果我是在开源中国上找到的 http ...

  7. html数字滚动选择,js实现数字滚动特效

    本文实例为大家分享了js实现数字滚动展示的具体代码,供大家参考,具体内容如下 效果图 html代码 Title #t,#tt{ border: #ccc thin solid; width: 250p ...

  8. 数字冰雹创始人邓潇专访:2017大数据可视化的关键技术及行业应用

    大数据可视化的内涵与意义 大数据可视化就是利用视觉的方式将那些巨大的.复杂的.枯燥的.潜逻辑的数据展现出来,无论通过地理空间.时间序列,还是逻辑关系等不同维度,最终使读者在短时间内理解数据背后的规律与 ...

  9. 什么是数字孪生?跟数据可视化的关系又是什么?

    大数据产业创新服务媒体 --聚焦数据 · 改变商业 2019年,"数字孪生"热度不断攀升,备受行业内外关注.各大峰会论坛将其作为热议主题,全球最具权威的IT研究与顾问咨询机构Gar ...

最新文章

  1. 【云栖大会】人工智能:智,在云端
  2. CentOS 6.3下源码安装LAMP(Linux+Apache+Mysql+Php)环境
  3. android简单的自定义按钮,Android 自定义button简单示例
  4. 无水印pdf编辑器_偷偷告诉你如何编辑PDF文件,轻松解决这万恶的千古难题
  5. SAP 电商云 Spartacus UI 同 SAP Customer Data Cloud 集成运行时的 api
  6. 设备驱动,字符设备驱动、(总线)设备驱动模型、sysfs文件系统、平台设备驱动
  7. android图片分辨率改变,android 通过修改图片像素实现CircleImageView
  8. .Net 转战 Android 4.4 日常笔记(1)--工具及环境搭建
  9. “37岁,年薪50万,一夜被裁”:伪上班,毁掉了多少中国年轻人
  10. 微服务在微信的架构实践
  11. matlab 脉冲压缩算法,雷达脉冲压缩matlab
  12. 了解了广告行业利益相关方,横扫一切商业模式
  13. 计算机一级证书英文 简历,通用于计算机英文简历范文
  14. “做我女朋友好吗?”vbs源码
  15. PTA数字金字塔(PTA怎么用以及代码怎么敲)
  16. python 字符串前面加 u, r, b的含义
  17. zipfile — 访问 ZIP 压缩文件
  18. 2021高考成绩位次查询6,江西高考排名对应学校-江西高考位次查询(2021年文科参考)...
  19. XML 文件加密与解密
  20. BUUCTF_Crypto_异性相吸(yxxx)

热门文章

  1. BSCI验厂RSP是什么意思?
  2. 【转】 SumaTra PDF 常用快捷键
  3. attention retain_retain
  4. Math-Model(三)高斯羽烟模型计算气体扩散浓度
  5. 启动计算机时会出现什么,为什么电脑启动的时候会出现很多英文字母?
  6. 一款开源免费、非常好用的的SSH/SFTP客户端Electerm
  7. 车辆转向不足转向过度个人总结
  8. CM部署(2):CM环境配置及安装
  9. IC类元件创建、接插件座子原件、晶体
  10. scrapy连接MySQL数据库爬取英雄联盟英雄传记