yunshangxie/tuniao-ui/components/tn-count-to/tn-count-to.vue

232 lines
6.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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