vue3使用vue-count-to元件的實現
阿新 • • 發佈:2020-12-27
專案場景:
資料視覺化大屏開發的過程中,需要實現一種滾動數字的效果,在使用vue2時,使用vue-count-to完全沒有問題,功能也比較完善(滾動時長,開始值,結束值,字首,字尾,千分隔符,小數分隔符等等),但是在vue3中使用會出現問題。
<template> <div id="nav"> <router-link to="/">Home</router-link> | <router-link to="/about">About</router-link> </div> <count-to :startVal="0" :endVal="2045" :duration="4000"></count-to> <router-view/> </template>
展示的效果
問題描述:
出現的錯誤時 == Cannot read property ‘_c' of undefined== 這是一個_c的屬性沒有找到,具體的情況也不是很清楚。在vue-count-to打包後的原始碼中可以大致看出來,這是在render函式中出現的錯誤。但是還是沒法下手。
解決方案:
採用的方法是直接複製node_modules下vue-count-to的原始檔(src下),到自己專案的components下。如圖
然後根據eslint的檢查,修改程式碼,直到不報錯,且記刪除package.json下剛剛引入的vue-count-to的依賴。如圖
最後重啟專案。
vue-count-to原始碼
let lastTime = 0 const prefixes = 'webkit moz ms o'.split(' ') // 各瀏覽器字首 let requestAnimationFrame let cancelAnimationFrame const isServer = typeof window === 'undefined' if (isServer) { requestAnimationFrame = function () { } cancelAnimationFrame = function () { } } 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(() => { const time = currTime + timeToCall callback(time) },timeToCall) lastTime = currTime + timeToCall return id } cancelAnimationFrame = function (id) { window.clearTimeout(id) } } } export { requestAnimationFrame,cancelAnimationFrame }
<template> <span> {{displayValue}} </span> </template> <script> import { requestAnimationFrame,cancelAnimationFrame } from './requestAnimationFrame.js' export default { props: { startVal: { type: Number,required: false,default: 0 },endVal: { type: Number,default: 2017 },duration: { type: Number,default: 3000 },autoplay: { type: Boolean,default: true },decimals: { type: Number,default: 0,validator (value) { return value >= 0 } },decimal: { type: String,default: '.' },separator: { type: String,default: ',' },prefix: { type: String,default: '' },suffix: { type: String,useEasing: { type: Boolean,easingFn: { type: Function,default (t,b,c,d) { return c * (-Math.pow(2,-10 * t / d) + 1) * 1024 / 1023 + b } } },data () { return { localStartVal: this.startVal,displayValue: this.formatNumber(this.startVal),printVal: null,paused: false,localDuration: this.duration,startTime: null,timestamp: null,remaining: null,rAF: null } },computed: { countDown () { return this.startVal > this.endVal } },watch: { startVal () { if (this.autoplay) { this.start() } },endVal () { if (this.autoplay) { this.start() } } },mounted () { if (this.autoplay) { this.start() } this.$emit('mountedCallback') },methods: { start () { this.localStartVal = this.startVal this.startTime = null this.localDuration = this.duration this.paused = false this.rAF = requestAnimationFrame(this.count) },pauseResume () { if (this.paused) { this.resume() this.paused = false } else { this.pause() this.paused = true } },pause () { cancelAnimationFrame(this.rAF) },resume () { this.startTime = null this.localDuration = +this.remaining this.localStartVal = +this.printVal requestAnimationFrame(this.count) },reset () { this.startTime = null cancelAnimationFrame(this.rAF) this.displayValue = this.formatNumber(this.startVal) },count (timestamp) { if (!this.startTime) this.startTime = timestamp this.timestamp = timestamp const progress = timestamp - this.startTime this.remaining = this.localDuration - progress if (this.useEasing) { if (this.countDown) { this.printVal = this.localStartVal - this.easingFn(progress,this.localStartVal - this.endVal,this.localDuration) } else { this.printVal = this.easingFn(progress,this.localStartVal,this.endVal - this.localStartVal,this.localDuration) } } else { if (this.countDown) { this.printVal = this.localStartVal - ((this.localStartVal - this.endVal) * (progress / this.localDuration)) } else { this.printVal = this.localStartVal + (this.endVal - this.localStartVal) * (progress / this.localDuration) } } if (this.countDown) { this.printVal = this.printVal < this.endVal ? this.endVal : this.printVal } else { this.printVal = this.printVal > this.endVal ? this.endVal : this.printVal } this.displayValue = this.formatNumber(this.printVal) if (progress < this.localDuration) { this.rAF = requestAnimationFrame(this.count) } else { this.$emit('callback') } },isNumber (val) { return !isNaN(parseFloat(val)) },formatNumber (num) { num = num.toFixed(this.decimals) num += '' const x = num.split('.') let x1 = x[0] const x2 = x.length > 1 ? this.decimal + x[1] : '' const rgx = /(\d+)(\d{3})/ if (this.separator && !this.isNumber(this.separator)) { while (rgx.test(x1)) { x1 = x1.replace(rgx,'$1' + this.separator + '$2') } } return this.prefix + x1 + x2 + this.suffix } },unmounted () { cancelAnimationFrame(this.rAF) } } </script>
到此這篇關於vue3使用vue-count-to元件的文章就介紹到這了,更多相關vue3 vue-count-to元件內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!