4 小时以前 533353dbeb19b3fa45817e9f65874db4e2ce6f9e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import { ref, watch, onScopeDispose } from 'vue'
 
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
 
// 数字滚动:只在值变化时做一次 500ms 缓动,尊重系统减弱动效设置
export function useCountUp(source, duration = 500) {
  const display = ref(0)
  let raf = 0
 
  const run = (to) => {
    const target = Number(to)
    if (!isFinite(target)) {
      display.value = 0
      return
    }
    if (reduceMotion || target === display.value) {
      display.value = target
      return
    }
    cancelAnimationFrame(raf)
    const from = Number(display.value) || 0
    const start = performance.now()
    const step = (now) => {
      const p = Math.min((now - start) / duration, 1)
      const eased = 1 - Math.pow(1 - p, 3)
      display.value = from + (target - from) * eased
      if (p < 1) raf = requestAnimationFrame(step)
    }
    raf = requestAnimationFrame(step)
  }
 
  watch(source, (v) => run(v), { immediate: true })
  onScopeDispose(() => cancelAnimationFrame(raf))
 
  return display
}
 
// 千分位格式化(保留整数或小数)
export function formatNum(n, digits) {
  const num = Number(n || 0)
  if (digits !== undefined) {
    return num.toLocaleString('zh-CN', { minimumFractionDigits: digits, maximumFractionDigits: digits })
  }
  return num.toLocaleString('zh-CN')
}