<template> <view class="tn-count-num-class tn-count-num" :class="[fontColorClass]" :style="{ fontSize: fontSizeStyle || '50rpx', fontWeight: bold ? 'bold' : 'normal', color: fontColorStyle || '#080808' }" > {{ displayValue }} </view> </template> <script> import componentsColorMixin from '../../libs/mixin/components_color.js' export default { name: 'tn-count-to', mixins: [componentsColorMixin], props: { // 开始的数值,默认为0 startVal: { type: Number, default: 0 }, // 结束目标数值 endVal: { type: Number, default: 0, required: true }, // 是否自动开始 autoplay: { type: Boolean, default: true }, // 滚动到目标值的持续时间,单位为毫秒 duration: { type: Number, default: 2000 }, // 是否在即将结束的时候使用缓慢滚动的效果 useEasing: { type: Boolean, default: true }, // 显示的小数位数 decimals: { type: Number, default: 0 }, // 十进制的分割符 decimalSeparator: { type: String, default: '.' }, // 千分位的分隔符 // 类似金额的分割(¥23,321.05中的",") thousandthsSeparator: { type: String, default: '' }, // 是否显示加粗字体 bold: { type: Boolean, default: false } }, computed: { countDown() { return this.startVal > this.endVal } }, data() { return { localStartVal: this.startVal, localDuration: this.duration, // 显示的数值 displayValue: this.formatNumber(this.startVal), // 打印的数值 printValue: null, // 是否暂停 paused: false, // 开始时间戳 startTime: null, // 停留时间戳 remainingTime: null, // 当前时间戳 timestamp: null, // 上一次的时间戳 lastTime: 0, rAF: null } }, watch: { startVal() { this.autoplay && this.start() }, endVal() { this.autoplay && this.start() } }, mounted() { this.autoplay && this.start() }, methods: { // 开始滚动 start() { this.localStartVal = this.startVal this.startTime = null this.localDuration = this.duration this.paused = false this.rAF = this.requestAnimationFrame(this.count) }, // 重新开始 reStart() { if (this.paused) { this.resume() this.paused = false } else { this.stop() this.paused = true } }, // 停止 stop() { this.cancelAnimationFrame(this.rAF) }, // 恢复 resume() { this.startTime = null this.localDuration = this.remainingTime this.localStartVal = this.printValue this.requestAnimationFrame(this.count) }, // 重置 reset() { this.startTime = null this.cnacelAnimationFrame(this.rAF) this.displayValue = this.formatNumber(this.startVal) }, // 销毁组件 destroyed() { this.cancelAnimationFrame(this.rAF) }, // 累加时间 count(timestamp) { if (!this.startTime) this.startTime = timestamp this.timestamp = timestamp const progress = timestamp - this.startTime this.remainingTime = this.localDuration - progress if (this.useEasing) { if (this.countDown) { this.printValue = this.localStartVal - this.easingFn(progress, 0, this.localStartVal - this.endVal, this.localDuration) } { this.printValue = this.easingFn(progress, this.localStartVal, this.endVal - this.localStartVal, this.localDuration) } } else { if (this.countDown) { this.printValue = this.localStartVal - (this.localStartVal - this.endVal) * (progress / this.localDuration) } else { this.printValue = this.localStartVal + (this.endVal - this.localStartVal) * (progress / this.localDuration) } } if (this.countDown) { this.printValue = this.printValue < this.endVal ? this.endVal : this.printValue } else { this.printValue = this.printValue > this.endVal ? this.endVal : this.printValue } this.displayValue = this.formatNumber(this.printValue) if (progress < this.localDuration) { this.rAF = this.requestAnimationFrame(this.count) } else { this.$emit('end') } }, // 缓动时间计算 easingFn(t, b, c, d) { return (c * (-Math.pow(2, (-10 * t) / d) + 1) * 1024) / 1023 + b }, // 请求帧动画 requestAnimationFrame(cb) { const currentTime = new Date().getTime() // 为了使setTimteout的尽可能的接近每秒60帧的效果 const timeToCall = Math.max(0, 16 - (currentTime - this.lastTime)) const timerId = setTimeout(() => { cb && cb(currentTime + timeToCall) }, timeToCall) this.lastTime = currentTime + timeToCall return timerId }, // 清除帧动画 clearAnimationFrame(timerId) { clearTimeout(timerId) }, // 格式化数值 formatNumber(number) { const reg = /(\d+)(\d{3})/ number = Number(number) number = number.toFixed(Number(this.decimals)) number += '' const numberArray = number.split('.') let num1 = numberArray[0] const num2 = numberArray.length > 1 ? this.decimalSeparator + numberArray[1] : '' if (this.thousandthsSeparator && !this.isNumber(this.thousandthsSeparator)) { while(reg.test(num1)) { num1 = num1.replace(reg, '$1' + this.thousandthsSeparator + '$2') } } return num1 + num2 }, // 判断是否为数字 isNumber(val) { return !isNaN(parseFloat(val)) } } } </script> <style lang="scss" scoped> .tn-count-num { /* #ifndef APP-NVUE */ display: inline-flex; /* #endif */ text-align: center; line-height: 1; } </style>