232 lines
6.3 KiB
Vue
232 lines
6.3 KiB
Vue
|
<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>
|