<template>
  <span class="number-animation" :class="{ 'with-unit': unit }" :style="style">
    {{ displayValue }}
    <span v-if="unit" class="unit" :style="unitStyle">{{ unit }}</span>
  </span>
</template>

<script setup lang="ts">
import { ref, onMounted, watch, computed } from 'vue'

interface Props {
  value: number | string
  decimals?: number
  thousands?: boolean
  duration?: number
  unit?: string
  style?: Record<string, string>
  unitStyle?: Record<string, string>
}

const props = withDefaults(defineProps<Props>(), {
  decimals: 0,
  thousands: false,
  duration: 1500,
  unit: '',
  style: () => ({}),
  unitStyle: () => ({}),
})

const displayValue = ref('0')
const parsedValue = computed(() => {
  if (typeof props.value === 'string') {
    // 处理带单位的字符串，如 "149.7万人"
    const numStr = props.value.replace(/[^0-9.]/g, '')
    return parseFloat(numStr) || 0
  }
  return props.value
})

const formatNumber = (num: number): string => {
  let result = num.toFixed(props.decimals)

  if (props.thousands) {
    const parts = result.split('.')
    parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',')
    result = parts.join('.')
  }

  return result
}

const animateValue = () => {
  const start = 0
  const end = parsedValue.value
  const duration = props.duration
  const startTime = performance.now()

  const animate = (currentTime: number) => {
    const elapsed = currentTime - startTime
    const progress = Math.min(elapsed / duration, 1)

    // 缓动函数：easeOutQuad
    const easeOutQuad = (t: number) => t * (2 - t)
    const easedProgress = easeOutQuad(progress)

    const currentValue = start + (end - start) * easedProgress
    displayValue.value = formatNumber(currentValue)

    if (progress < 1) {
      requestAnimationFrame(animate)
    }
  }

  requestAnimationFrame(animate)
}

watch(
  () => props.value,
  () => {
    animateValue()
  },
  { deep: true, immediate: true }
)

onMounted(() => {
  animateValue()
})
</script>

<style lang="scss" scoped>
.number-animation {
  display: inline-block;
  white-space: nowrap;

  &.with-unit {
    display: flex;
    align-items: baseline;

    .unit {
      margin-left: 4px;
    }
  }
}
</style>
