Explorar el Código

增加数字动画显示,提升逼格

wukai hace 1 mes
padre
commit
5a8d0cb684
Se han modificado 1 ficheros con 90 adiciones y 0 borrados
  1. 90 0
      src/pages/components/NumberAnimation.vue

+ 90 - 0
src/pages/components/NumberAnimation.vue

@@ -0,0 +1,90 @@
+<template>
+  <span>{{ formattedNumber }}</span>
+</template>
+
+<script>
+import { ref, onMounted, watch, computed } from 'vue';
+
+export default {
+  name: 'NumberAnimation',
+  props: {
+    value: {
+      type: [Number, String],
+      default: 0
+    },
+    duration: {
+      type: Number,
+      default: 3000
+    },
+    decimal: {
+      type: Number,
+      default: 0
+    }
+  },
+  setup(props) {
+    const currentValue = ref(0);
+    let startTime = 0;
+    let animationFrameId = null;
+    let targetValue = 0;
+    let startValue = 0;
+
+    const formattedNumber = computed(() => {
+      if (props.decimal === 0) {
+        return Math.round(currentValue.value).toLocaleString();
+      } else {
+        return currentValue.value.toLocaleString(undefined, {
+          minimumFractionDigits: props.decimal,
+          maximumFractionDigits: props.decimal
+        });
+      }
+    });
+
+    const animate = (timestamp) => {
+      if (!startTime) startTime = timestamp;
+      const elapsed = timestamp - startTime;
+      const progress = Math.min(elapsed / props.duration, 1);
+
+      // 使用easeOutQuad缓动函数
+      const easeOutQuad = 1 - Math.pow(1 - progress, 2);
+      currentValue.value = startValue + (targetValue - startValue) * easeOutQuad;
+
+      if (progress < 1) {
+        animationFrameId = requestAnimationFrame(animate);
+      }
+    };
+
+    const startAnimation = (newValue) => {
+      // 取消之前的动画
+      if (animationFrameId) {
+        cancelAnimationFrame(animationFrameId);
+      }
+
+      startValue = currentValue.value;
+      targetValue = typeof newValue === 'string' ? parseFloat(newValue) || 0 : newValue;
+      startTime = 0;
+      animationFrameId = requestAnimationFrame(animate);
+    };
+
+    watch(() => props.value, (newValue) => {
+      startAnimation(newValue);
+    }, { immediate: true });
+
+    onMounted(() => {
+      currentValue.value = typeof props.value === 'string' ? parseFloat(props.value) || 0 : props.value;
+    });
+
+    return {
+      currentValue,
+      formattedNumber
+    };
+  }
+};
+</script>
+
+<style scoped>
+span {
+  display: inline-block;
+  min-width: 40px;
+  text-align: right;
+}
+</style>