UNPKG

1.82 kBPlain TextView Raw
1<script>
2export default {
3 name: 'AnimatedNumber',
4 props: {
5 number: {
6 type: Number,
7 required: true,
8 },
9 /**
10 * Controls how long it takes for the animation to complete.
11 */
12 duration: {
13 type: Number,
14 required: false,
15 default: 2000,
16 },
17 /**
18 * Controls the number of decimal places displayed in the output.
19 */
20 decimalPlaces: {
21 type: Number,
22 required: false,
23 default: 0,
24 },
25 animateOnMount: {
26 type: Boolean,
27 required: false,
28 default: false,
29 },
30 },
31 data() {
32 return {
33 displayNumber: 0,
34 startTime: null,
35 };
36 },
37 computed: {
38 animatedNumber() {
39 return this.displayNumber.toFixed(this.decimalPlaces);
40 },
41 },
42 ready() {
43 this.displayNumber = this.number ? this.number : 0;
44 },
45 watch: {
46 number() {
47 this.animate();
48 },
49 },
50 mounted() {
51 if (this.animateOnMount) {
52 this.animate();
53 } else {
54 this.displayNumber = this.number;
55 }
56 },
57 methods: {
58 animate() {
59 this.$emit('animating');
60 window.requestAnimationFrame(this.count);
61 },
62 count(timestamp) {
63 if (!this.startTime) {
64 this.startTime = timestamp;
65 }
66
67 const progress = timestamp - this.startTime;
68
69 if (progress < this.duration) {
70 if (this.displayNumber !== this.number) {
71 const change = (this.number - this.displayNumber) / (this.duration / 100);
72 this.displayNumber += change;
73 }
74 window.requestAnimationFrame(this.count);
75 } else {
76 this.displayNumber = this.number; // Ensures that the final number is accurate.
77 this.startTime = null;
78 this.$emit('animated');
79 }
80 },
81 },
82};
83</script>
84<template>
85 <span>{{ animatedNumber }}</span>
86</template>