count-to.vue 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. <script lang="ts" setup>
  2. import type { CountToProps } from './types';
  3. import { computed, onMounted, ref, watch } from 'vue';
  4. import { isString } from '@vben-core/shared/utils';
  5. import { TransitionPresets, useTransition } from '@vueuse/core';
  6. const props = withDefaults(defineProps<CountToProps>(), {
  7. startVal: 0,
  8. duration: 2000,
  9. separator: ',',
  10. decimal: '.',
  11. decimals: 0,
  12. delay: 0,
  13. transition: () => TransitionPresets.easeOutExpo,
  14. });
  15. const emit = defineEmits(['started', 'finished']);
  16. const lastValue = ref(props.startVal);
  17. onMounted(() => {
  18. lastValue.value = props.endVal;
  19. });
  20. watch(
  21. () => props.endVal,
  22. (val) => {
  23. lastValue.value = val;
  24. },
  25. );
  26. const currentValue = useTransition(lastValue, {
  27. delay: computed(() => props.delay),
  28. duration: computed(() => props.duration),
  29. disabled: computed(() => props.disabled),
  30. transition: computed(() => {
  31. return isString(props.transition)
  32. ? TransitionPresets[props.transition]
  33. : props.transition;
  34. }),
  35. onStarted() {
  36. emit('started');
  37. },
  38. onFinished() {
  39. emit('finished');
  40. },
  41. });
  42. const numMain = computed(() => {
  43. const result = currentValue.value
  44. .toFixed(props.decimals)
  45. .split('.')[0]
  46. ?.replaceAll(/\B(?=(\d{3})+(?!\d))/g, props.separator);
  47. return result;
  48. });
  49. const numDec = computed(() => {
  50. return (
  51. props.decimal + currentValue.value.toFixed(props.decimals).split('.')[1]
  52. );
  53. });
  54. </script>
  55. <template>
  56. <div class="count-to" v-bind="$attrs">
  57. <slot name="prefix">
  58. <div
  59. class="count-to-prefix"
  60. :style="prefixStyle"
  61. :class="prefixClass"
  62. v-if="prefix"
  63. >
  64. {{ prefix }}
  65. </div>
  66. </slot>
  67. <div class="count-to-main" :class="mainClass" :style="mainStyle">
  68. <span>{{ numMain }}</span>
  69. <span
  70. class="count-to-main-decimal"
  71. v-if="decimals > 0"
  72. :class="decimalClass"
  73. :style="decimalStyle"
  74. >
  75. {{ numDec }}
  76. </span>
  77. </div>
  78. <slot name="suffix">
  79. <div
  80. class="count-to-suffix"
  81. :style="suffixStyle"
  82. :class="suffixClass"
  83. v-if="suffix"
  84. >
  85. {{ suffix }}
  86. </div>
  87. </slot>
  88. </div>
  89. </template>
  90. <style lang="scss" scoped>
  91. .count-to {
  92. display: flex;
  93. align-items: baseline;
  94. &-prefix {
  95. // font-size: 1rem;
  96. }
  97. &-suffix {
  98. // font-size: 1rem;
  99. }
  100. &-main {
  101. font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  102. // font-size: 1.5rem;
  103. &-decimal {
  104. // font-size: 0.8rem;
  105. }
  106. }
  107. }
  108. </style>