70 lines
1.3 KiB
Vue
70 lines
1.3 KiB
Vue
|
|
<template>
|
||
|
|
<span>{{ prefix }}{{ displayValue }}{{ suffix }}</span>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script setup>
|
||
|
|
import { ref, onMounted, watch } from 'vue'
|
||
|
|
|
||
|
|
const props = defineProps({
|
||
|
|
endVal: {
|
||
|
|
type: Number,
|
||
|
|
required: true
|
||
|
|
},
|
||
|
|
duration: {
|
||
|
|
type: Number,
|
||
|
|
default: 2
|
||
|
|
},
|
||
|
|
prefix: {
|
||
|
|
type: String,
|
||
|
|
default: ''
|
||
|
|
},
|
||
|
|
suffix: {
|
||
|
|
type: String,
|
||
|
|
default: ''
|
||
|
|
},
|
||
|
|
decimals: {
|
||
|
|
type: Number,
|
||
|
|
default: 0
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
const displayValue = ref(0)
|
||
|
|
|
||
|
|
const countUp = () => {
|
||
|
|
const startTime = Date.now()
|
||
|
|
const startVal = 0
|
||
|
|
const endVal = props.endVal
|
||
|
|
const duration = props.duration * 1000
|
||
|
|
|
||
|
|
const updateCount = () => {
|
||
|
|
const now = Date.now()
|
||
|
|
const progress = Math.min((now - startTime) / duration, 1)
|
||
|
|
|
||
|
|
// Easing function for smooth animation
|
||
|
|
const easeOutQuart = 1 - Math.pow(1 - progress, 4)
|
||
|
|
|
||
|
|
const currentVal = startVal + (endVal - startVal) * easeOutQuart
|
||
|
|
displayValue.value = props.decimals > 0
|
||
|
|
? currentVal.toFixed(props.decimals)
|
||
|
|
: Math.round(currentVal).toLocaleString()
|
||
|
|
|
||
|
|
if (progress < 1) {
|
||
|
|
requestAnimationFrame(updateCount)
|
||
|
|
} else {
|
||
|
|
displayValue.value = props.decimals > 0
|
||
|
|
? endVal.toFixed(props.decimals)
|
||
|
|
: endVal.toLocaleString()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
updateCount()
|
||
|
|
}
|
||
|
|
|
||
|
|
onMounted(() => {
|
||
|
|
countUp()
|
||
|
|
})
|
||
|
|
|
||
|
|
watch(() => props.endVal, () => {
|
||
|
|
countUp()
|
||
|
|
})
|
||
|
|
</script>
|