返回顶部
首页 > 资讯 > 前端开发 > JavaScript >vue3数据可视化实现数字滚动特效代码
  • 659
分享到

vue3数据可视化实现数字滚动特效代码

2024-04-02 19:04:59 659人浏览 八月长安
摘要

目录前言思路文件目录使用示例入口文件index.jsmain.js使用requestAnimationFrame.js思路完整代码:CountTo.Vue组件思路总结前言 vue3不

前言

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思路

  • 先判断是不是浏览器还是其他环境
  • 如果是浏览器判断浏览器内核类型
  • 如果浏览器不支持requestAnimationFrame,cancelAnimationFrame方法,改写setTimeout定时器
  • 导出两个方法 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,则会退到setTimeout
    if (!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 + timeToCall
            return 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.requestAnimationFrame
    cancelAnimationFrame = window.cancelAnimationFrame
    let 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,则会退到setTimeout
    if (!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 + timeToCall
            return 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.start
  state.startTime = null
  state.localDuration = props.duration
  state.paused = false
  state.rAF = requestAnimationFrame(count)
}

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

  if (!state.startTime) state.startTime = timestamp
  state.timestamp = timestamp
  const progress = timestamp - state.startTime
  state.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.start
  state.startTime = null
  state.localDuration = props.duration
  state.paused = false
  state.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 = null
  state.localDuration = +state.remaining
  state.localStart = +state.printVal
  requestAnimationFrame(count)
}

const pauseResume = () => {
  if (state.paused) {
    resume()
    state.paused = false
  } else {
    pause()
    state.paused = true
  }
}

const reset = () => {
  state.startTime = null
  cancelAnimationFrame(state.rAF)
  state.displayValue = formatNumber(props.start)
}

const count = (timestamp) => {
  if (!state.startTime) state.startTime = timestamp
  state.timestamp = timestamp
  const progress = timestamp - state.startTime
  state.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演示

到此这篇关于vue3数据可视化实现数字滚动特效的文章就介绍到这了,更多相关vue3数据可视化内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: vue3数据可视化实现数字滚动特效代码

本文链接: https://lsjlt.com/news/167506.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • vue3数据可视化实现数字滚动特效代码
    目录前言思路文件目录使用示例入口文件index.jsmain.js使用requestAnimationFrame.js思路完整代码:CountTo.vue组件思路总结前言 vue3不...
    99+
    2024-04-02
  • vue3实现数字滚动特效实例详解
    目录前言思路文件目录使用示例入口文件index.jsmain.js使用requestAnimationFrame.js思路完整代码:CountTo.vue组件思路总结前言 vue3不...
    99+
    2024-04-02
  • pandas实现数据可视化的示例代码
    目录一、概述1.1 plot函数参数1.2 本文用到的数据源说明二、折线图--kind='line'三、柱状图--kind='bar'3.1 各组数据...
    99+
    2024-04-02
  • Python实现交通数据可视化的示例代码
    目录1、TransBigData简介2、数据预处理3、数据栅格化4、订单起讫点OD提取与聚合集计5、交互可视化1、TransBigData简介 TransBigData是一个为交通...
    99+
    2023-05-17
    Python交通数据可视化 Python数据可视化 Python可视化
  • 基于springboot实现数据可视化的示例代码
    目录前言:一、读取Excel表格中的数据二、采用柱形图显示Excel表格数据2.1 前端代码2.2 后端代码三、采用饼状图显示Excel表格数据3.1 前端代码3.2 后端代码 参...
    99+
    2024-04-02
  • pyecharts实现数据可视化
    目录1.概述2.安装3.数据可视化代码3.1 柱状图3.2 折线图3.3 饼图1.概述 pyecharts 是百度开源的,适用于数据可视化的工具,配置灵活,展示图表相对美观,顺滑。 ...
    99+
    2024-04-02
  • Vue3基于countUp.js实现数字滚动的插件
    目录countUp 简介countUp 组件封装文末countUp 简介 CountUp.js 是一种无依赖项的轻量级 JavaScript 类,可用于快速创建以更有趣的方式显示数字...
    99+
    2023-05-17
    Vue3 countUp.js数字滚动 Vue3 数字滚动
  • JavaScript实现余额数字滚动效果
    目录1.实现背景2.实现思路3.实现过程1.实现背景 上周在一个完成任务领取红包的活动需求中,需要实现一个用户点击按钮弹出领取红包弹窗后,在关 闭弹窗返回原来的页面时,页面余额数字...
    99+
    2024-04-02
  • jquery数字滚动效果怎么实现
    您可以使用jQuery的.animate()方法来实现数字滚动效果。首先,您需要一个HTML元素来显示数字。例如,一个div元素:`...
    99+
    2023-08-09
    jquery
  • python数字滚动效果怎么实现
    要实现数字滚动效果,可以使用Python的Tkinter库来创建一个简单的窗口应用程序。以下是一个示例代码,演示如何实现数字滚动效果...
    99+
    2024-03-01
    python
  • Vue金融数字格式化(并保留小数)数字滚动效果实现
    目录Vue金融数字格式化(并保留小数) 数字滚动补充:vue做数字滚动效果用vue-countTo实现安装使用Vue金融数字格式化(并保留小数) 数字滚动  提示 &nb...
    99+
    2023-05-16
    vue数字滚动 vue金融数字格式化
  • Qt实现字幕滚动效果的示例代码
    目录一、项目介绍二、项目基本配置三、UI界面设计四、主程序实现4.1 widget.h头文件4.2 widget.cpp源文件五、效果演示一、项目介绍 利用QTimer实现字幕滚动功...
    99+
    2024-04-02
  • Python股票数据可视化代码详解
    目录数据准备阿里巴巴谷歌苹果腾讯亚马逊Facebook数据可视化查看各个公司的股价平均值查看各公司股价分布情况股价走势对比总结import numpy as np import pa...
    99+
    2024-04-02
  • 利用Python进行数据可视化的实例代码
    目录前言首先搭建环境实例代码例子1: 例子2: 例子3: 例子4: 例子5: 例子6:总结前言 前面写过一篇用Python制作PPT...
    99+
    2024-04-02
  • pyecharts如何实现数据可视化
    这篇文章将为大家详细讲解有关pyecharts如何实现数据可视化,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。1.概述pyecharts 是百度开源的,适用于数据可视化的工具,配置灵活,展示图表相对美观,...
    99+
    2023-06-29
  • vue可视化大屏实现无线滚动列表飞入效果
    目录一、效果如下二、代码如下(因项目是vite与vue3.0、element-plus)一、效果如下 二、代码如下(因项目是vite与vue3.0、element-plus) &l...
    99+
    2024-04-02
  • Python数据可视化Pyecharts库实现桑葚图效果
    目录基本思路我总结大概有三步:1. 先申明使用sankey2. 使用add 添加对sankey图的配置信息3. 最后render生成html文件展示首先介绍一下什么是桑葚图? 桑基图...
    99+
    2024-04-02
  • Python数据可视化正态分布简单分析及实现代码
    Python说来简单也简单,但是也不简单,尤其是再跟高数结合起来的时候。。。 正态分布(Normaldistribution),也称“常态分布”,又名高斯分布(Gaussiandistribution),最...
    99+
    2022-06-04
    正态分布 代码 简单
  • Vue组件实现数字滚动抽奖效果
    本文实例为大家分享了Vue组件实现数字滚动抽奖效果的具体代码,供大家参考,具体内容如下 可调整数字滚动速度,可指定开奖延迟时间,可指定开奖数字,按个人需求自行改动(录了个效果供参考,...
    99+
    2024-04-02
  • JavaScript怎么实现余额数字滚动效果
    这篇文章主要介绍“JavaScript怎么实现余额数字滚动效果”,在日常操作中,相信很多人在JavaScript怎么实现余额数字滚动效果问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作