218 lines
5.2 KiB
Vue
218 lines
5.2 KiB
Vue
<template>
|
|
<text
|
|
class="uv-count-num"
|
|
:style="textStyle"
|
|
>
|
|
{{ displayValue }}
|
|
</text>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
name: 'uv-count-to',
|
|
props: {
|
|
startVal: {
|
|
type: [String, Number],
|
|
default: 0
|
|
},
|
|
endVal: {
|
|
type: [String, Number],
|
|
default: 0
|
|
},
|
|
duration: {
|
|
type: [String, Number],
|
|
default: 2000
|
|
},
|
|
autoplay: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
decimals: {
|
|
type: [String, Number],
|
|
default: 0
|
|
},
|
|
useEasing: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
decimal: {
|
|
type: [String, Number],
|
|
default: '.'
|
|
},
|
|
color: {
|
|
type: String,
|
|
default: '#606266'
|
|
},
|
|
fontSize: {
|
|
type: [String, Number],
|
|
default: 22
|
|
},
|
|
bold: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
separator: {
|
|
type: String,
|
|
default: ''
|
|
}
|
|
},
|
|
data() {
|
|
return {
|
|
localStartVal: this.startVal,
|
|
displayValue: this.formatNumber(this.startVal),
|
|
printVal: null,
|
|
paused: false,
|
|
localDuration: Number(this.duration),
|
|
startTime: null,
|
|
timestamp: null,
|
|
remaining: null,
|
|
rAF: null,
|
|
lastTime: 0
|
|
}
|
|
},
|
|
computed: {
|
|
countDown() {
|
|
return this.startVal > this.endVal
|
|
},
|
|
textStyle() {
|
|
return {
|
|
fontSize: this.fontSize + 'px',
|
|
fontWeight: this.bold ? 'bold' : 'normal',
|
|
color: this.color
|
|
}
|
|
}
|
|
},
|
|
watch: {
|
|
startVal() {
|
|
if (this.autoplay) {
|
|
this.start()
|
|
}
|
|
},
|
|
endVal() {
|
|
console.log(123)
|
|
if (this.autoplay) {
|
|
this.start()
|
|
}
|
|
}
|
|
},
|
|
mounted() {
|
|
if (this.autoplay) {
|
|
this.start()
|
|
}
|
|
},
|
|
methods: {
|
|
easingFn(t, b, c, d) {
|
|
return c * (1 - Math.pow(2, -10 * t / d)) * 1024 / 1023 + b
|
|
},
|
|
requestAnimationFrame(callback) {
|
|
const currTime = new Date().getTime()
|
|
const timeToCall = Math.max(0, 16 - (currTime - this.lastTime))
|
|
const id = setTimeout(() => {
|
|
callback(currTime + timeToCall)
|
|
}, timeToCall)
|
|
this.lastTime = currTime + timeToCall
|
|
return id
|
|
},
|
|
cancelAnimationFrame(id) {
|
|
clearTimeout(id)
|
|
},
|
|
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)
|
|
this.paused = true
|
|
},
|
|
resume() {
|
|
if (this.remaining) {
|
|
this.startTime = 0
|
|
this.localDuration = this.remaining
|
|
this.localStartVal = this.printVal
|
|
this.requestAnimationFrame(this.count)
|
|
}
|
|
},
|
|
reset() {
|
|
this.startTime = null
|
|
this.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, 0, 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) || 0
|
|
|
|
if (progress < this.localDuration) {
|
|
this.rAF = this.requestAnimationFrame(this.count)
|
|
} else {
|
|
this.$emit('end')
|
|
}
|
|
},
|
|
isNumber(val) {
|
|
return !isNaN(parseFloat(val))
|
|
},
|
|
formatNumber(num) {
|
|
num = Number(num)
|
|
num = num.toFixed(Number(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 x1 + x2
|
|
}
|
|
},
|
|
destroyed() {
|
|
this.cancelAnimationFrame(this.rAF)
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.uv-count-num {
|
|
display: inline-flex;
|
|
text-align: center;
|
|
}
|
|
</style> |