目录前言思路文件目录使用示例入口文件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" // 后缀符号
/>
const UILib = {
install(Vue) {
Vue.component('CountTo', CountTo)
}
}
export default UILib
import CountTo from './components/count-to/index';
app.use(CountTo)
各个浏览器前缀: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 }
首先引入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
2024-01-12
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0