QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3847|回复: 0
打印 上一主题 下一主题

[其他资源] vue3 | 数据可视化实现数字滚动特效

[复制链接]
字体大小: 正常 放大
杨利霞        

5273

主题

82

听众

17万

积分

  • TA的每日心情
    开心
    2021-8-11 17:59
  • 签到天数: 17 天

    [LV.4]偶尔看看III

    网络挑战赛参赛者

    网络挑战赛参赛者

    自我介绍
    本人女,毕业于内蒙古科技大学,担任文职专业,毕业专业英语。

    群组2018美赛大象算法课程

    群组2018美赛护航培训课程

    群组2019年 数学中国站长建

    群组2019年数据分析师课程

    群组2018年大象老师国赛优

    跳转到指定楼层
    1#
    发表于 2022-9-14 17:02 |只看该作者 |倒序浏览
    |招呼Ta 关注Ta
    vue3 | 数据可视化实现数字滚动特效9 y# O1 E6 W9 |) N1 C$ V& d/ q

    * M" F' P: ^5 |# T8 G前言1 s$ i) F9 w2 [) d2 n5 W
    vue3不支持vue-count-to插件,无法使用vue-count-to实现数字动效,数字自动分割,vue-count-to主要针对vue2使用,vue3按照会报错:
    7 U0 Y- Y$ `7 M. s1 ^# A) UTypeError: Cannot read properties of undefined (reading '_c')# `( d7 |) V2 Q9 f0 z. M
    的错误信息。这个时候我们只能自己封装一个CountTo组件实现数字动效。先来看效果图:$ n, ?/ T$ r& O  q) i
    7 }( o/ ^( b9 a* E) _/ U! T

    9 H0 D& X2 M) M思路
    ' {3 _0 b8 ^% C% p4 T! u" I使用Vue.component定义公共组件,使用window.requestAnimationFrame(首选,次选setTimeout)来循环数字动画,window.cancelAnimationFrame取消数字动画效果,封装一个requestAnimationFrame.js公共文件,CountTo.vue组件,入口导出文件index.js。, r, H% d+ ~+ E, T1 t3 }' U

    . E8 \- a4 o/ [* X% m+ Z8 q, U/ V7 g文件目录1 {8 }7 E) J* }% f( j9 {

    8 [- w! z1 p+ `3 n* f2 W+ E6 x. Y9 @# G
    使用示例, [! v2 F7 p2 ^" A8 M. P2 g7 \0 _  l
    <CountTo, h- \; }6 c0 H3 o
    :start="0" // 从数字多少开始: B  u2 Q# Y3 k3 E4 x
    :end="endCount" // 到数字多少结束
    5 [- V+ |  b/ {, D :autoPlay="true" // 自动播放
    & R* Y# h: r" J; T' m :duration="3000" // 过渡时间
    6 m1 `2 f) U/ w5 W: t- `! _' ~# U prefix="¥"   // 前缀符号3 a/ i" t6 O0 ^$ e
    suffix="rmb" // 后缀符号
    . @4 y5 N$ _: g. h0 b. }8 i) b, T6 u />
    , S9 w) H! N$ K4 o% f: F: Q3 V+ J) P1
    $ O# Y* w* P1 _) C; e. z! R% Y2) F2 e+ a6 V& w( [# M$ p
    3
    + m. _) }- V" D# ^; X4
    8 ~5 q5 B( t9 [& x$ C5 J5# z  r: ]/ U- @6 p4 ?
    6
    9 Y2 v7 |5 I, ^, J: B! Y74 X5 l" q8 o8 S5 g
    8* Q$ P1 z. o! e/ ?! S" e, E# v: _
    入口文件index.js1 K# P1 K. v* A+ Q- t% R: S  b
    2 y# w' n! ^. [
    const UILib = {/ `+ y$ I# S8 n/ E  M  H
      install(Vue) {/ s- t& z8 y, w5 p
        Vue.component('CountTo', CountTo)
    0 G" K1 h3 p! i3 Q( k8 C  }0 `" D+ N; j0 ~8 u" R6 a
    }  q) U; r; _2 a* j2 h4 r
    4 }" Q& ~  @6 `  e7 K$ O  e8 ~: _% o
    export default UILib
      J7 A2 i# V/ M1 c3 e, ]: k( {: c0 W, k% i/ n1 ^
    18 q8 ^& U3 l$ t2 {+ D& g
    2
    7 P/ ]9 i; G+ c, ]; m3
    # r* T* K$ e% w6 ^7 y% E! B* ~3 K4
    5 Y6 l; \4 t' s" r3 T5
    3 r* U( v) {5 u8 V: y4 H, ^6
    . m; w$ @0 U, x; S7
    ; o  P% u- S& I8 {9 [+ r8& c% C% o0 R+ c; ]. b+ @
    9
    ( |& g) [9 G( a  C  r6 I+ }; }: W& xmain.js使用
    ' r* G7 T8 q% ~, cimport CountTo from './components/count-to/index';8 `, S" B6 x- V! T+ }9 W- {3 S
    app.use(CountTo)  n, Z8 k: L* F( _
    1
    1 x5 P2 F2 ]6 K1 N0 F2- V, ^( R( t: I) o% t4 D* P" a
    requestAnimationFrame.js思路
    5 T8 o9 y. k: u  ?  ]9 P先判断是不是浏览器还是其他环境& Y" D1 [+ Y! V% q
    如果是浏览器判断浏览器内核类型
    " K7 V) f3 O* |如果浏览器不支持requestAnimationFrame,cancelAnimationFrame方法,改写setTimeout定时器
    ! a; U: y3 {$ C. b  ]+ k0 d导出两个方法 requestAnimationFrame, cancelAnimationFrame
    ' H9 t. h  h2 h  ]) i各个浏览器前缀:let prefixes =  'webkit moz ms o';
    . g% x9 G! l; U" T判断是不是浏览器:let isServe = typeof window == 'undefined';
    % z4 N+ }. F* H0 l+ `6 x  h/ ]& U增加各个浏览器前缀:  , F0 i! Q( N% q  C* f. a, U0 V  f
    let prefix;+ K9 n9 J2 {% n% ]0 O  {5 U& N
    let requestAnimationFrame;2 \1 \+ W  Z3 H; t
    let cancelAnimationFrame;1 Y1 d8 ~( b; Z2 ]: v5 F/ ^* M
    // 通过遍历各浏览器前缀,来得到requestAnimationFrame和cancelAnimationFrame在当前浏览器的实现形式, I( o  e6 Y: m' l7 [% a0 U
        for (let i = 0; i < prefixes.length; i++) {
    - o& U" X+ R$ O; H        if (requestAnimationFrame && cancelAnimationFrame) { break }5 u8 K/ b" C$ _* x% j" T
            prefix = prefixes
    & ?9 z9 b8 r3 O        requestAnimationFrame = requestAnimationFrame || window[prefix + 'RequestAnimationFrame']' A& ~9 }: ]4 Z/ z* A
            cancelAnimationFrame = cancelAnimationFrame || window[prefix + 'CancelAnimationFrame'] || window[prefix + 'CancelRequestAnimationFrame']3 g0 y% m6 w( f$ l
        }
    : b9 j# }* X8 v! z% U' F7 j8 z7 l& P: x2 r
      //不支持使用setTimeout方式替换:模拟60帧的效果
    ' h9 ~# E& s0 i8 {7 `3 ]3 f. S  // 如果当前浏览器不支持requestAnimationFrame和cancelAnimationFrame,则会退到setTimeout
    " s( H+ E9 c* s, |* t0 v5 U    if (!requestAnimationFrame || !cancelAnimationFrame) {3 r6 T  ^/ q0 j9 O4 Y- U2 [/ {4 k
            requestAnimationFrame = function (callback) {
    # r0 ]& K% |2 p; A; w8 O            const currTime = new Date().getTime()
    , `6 k' ]5 N0 c1 V9 h3 `6 o            // 为了使setTimteout的尽可能的接近每秒60帧的效果6 l' z+ O8 g4 W! {3 n( k7 U
                const timeToCall = Math.max(0, 16 - (currTime - lastTime))
    5 a& s7 H$ G' Q1 Y  B2 ]$ }            const id = window.setTimeout(() => {2 u3 N& e" h8 b! d- V0 `1 l
                    callback(currTime + timeToCall)
    . B! t. K* y1 z0 h# s2 e  s            }, timeToCall)( B5 j% Z, h7 _4 H# S7 x; p, _3 T
                lastTime = currTime + timeToCall
    1 ^4 Z8 L: H8 V! n' ?* u& u            return id
    7 I/ C9 V3 N' U0 X8 ]        }8 \' b- ~  q" y7 W
    ' l7 j6 V: ]$ ^8 J
            cancelAnimationFrame = function (id) {/ K/ S$ H  Y2 o" i! T. ~& _
                window.clearTimeout(id)/ @' Y1 C" V6 |& |
            }7 B! L  R/ J2 a" G7 X* y/ {4 O" W" q  i
        }
    7 c" g6 {. _9 N6 f' t+ B% E/ t3 s3 @  L0 c/ B) N  b& X
    17 ]7 ^/ I( V  \2 ?- P
    2; E' \9 @8 A  e
    3! ?! l& G. U9 {0 ?) `
    46 d. F# {) e) }8 ^
    5
    % ^2 W% w. z& i6/ ?! w$ l: U" p: S0 M! s
    7
    + n& `7 P0 p: L3 c9 Z8
    " ~$ h" a, N( ]0 D/ e  t6 p$ L9 Z9
    ( ^( u4 T% W$ @3 X7 t10
    + ^, S% f; \3 @1 _117 g2 _4 O* B: ~, f% O; D0 A
    12: d9 n' h$ Q" j) ]  q
    13
    1 ?7 u( Y, G/ h; A14: W, P% @) J  c9 F1 O2 Z% k. }% ]
    15
    2 W- j  `+ a" Q( D16  \8 z' l# E2 @
    174 @8 w2 @3 f; O1 m) ]1 @( b; s
    182 \' F0 _4 T0 ^
    19
    9 D, x$ U. H4 V% ~4 V20' A& i" c7 o7 Y$ R2 v& F( o
    21! a$ P8 Z- B; L* C# ?* Z/ e' B7 V
    22
    " S# O9 z7 W( \23
    , _, b' ]4 n% N0 e! L24
    ) d+ W% N$ |2 U/ C8 _# G, l25
    * @' r7 e" H5 Q6 w4 Y# a5 u+ f26
    2 _) E5 E% m7 Q+ o) Q27
    4 p' y/ v2 N0 _! T6 Z3 u+ N28" t- o9 l6 t( A; m. o, j& N, M
    29
    & t+ T4 p. G# W& q6 r, A# L* v30
    8 S# l4 E  d, o* |2 o4 B315 Y, `. ~. S; c
    32
    $ D+ E' [5 D) g4 R8 ]" l完整代码:* u* ]4 A4 A/ u" T6 _' J- w
    requestAnimationFrame.js
    8 j9 C6 G2 R7 v& z) ?- Z0 r- Q! t* U) N4 G2 v0 C- D) j
    let lastTime = 0
    ( B* ?4 B& j. `  {" J7 V# fconst prefixes = 'webkit moz ms o'.split(' ') // 各浏览器前缀( O5 @% M, p: Z4 H; ^) O- ]
    ) X, g: q3 D- t/ B7 ^
    let requestAnimationFrame
    ) M7 S9 b5 U) W) b; ]let cancelAnimationFrame1 K2 @" [3 A' H1 V- U; {

    5 K) n( ]/ K, G0 a// 判断是否是服务器环境( L; d" v3 n  w8 m; ?  W$ Z# N
    const isServer = typeof window === 'undefined'
    , [4 l$ C, t! l: n1 Iif (isServer) {
    6 B4 r  t% E) Q0 M+ {7 l    requestAnimationFrame = function () {
    ' |9 Y: x2 R, l5 J, E/ F" F        return* o! M- k  t: E
        }$ O# Q1 F& c* v) i
        cancelAnimationFrame = function () {% G, e# D% }; n1 i+ h
            return
    & L2 y1 [& w* v6 _5 S    }
    " L% I% O' O0 k} else {2 I( \+ D. y8 d
        requestAnimationFrame = window.requestAnimationFrame
    8 l7 E% k1 w/ g' v6 _+ F8 v' T    cancelAnimationFrame = window.cancelAnimationFrame( L$ O7 J+ w2 m, v
        let prefix) ?: E( O6 k& o
        // 通过遍历各浏览器前缀,来得到requestAnimationFrame和cancelAnimationFrame在当前浏览器的实现形式
    4 _; }/ ~) v6 v) d    for (let i = 0; i < prefixes.length; i++) {
    $ Y8 Y! m! e% x( q# \, }, @        if (requestAnimationFrame && cancelAnimationFrame) { break }
    : ^( m& R' G' A$ }# f! h9 s4 ~8 H0 `        prefix = prefixes% D& F7 p8 J) ^- W# H8 x, l
            requestAnimationFrame = requestAnimationFrame || window[prefix + 'RequestAnimationFrame']# F- D2 B* N; w& }" V/ a0 }- \" q! o
            cancelAnimationFrame = cancelAnimationFrame || window[prefix + 'CancelAnimationFrame'] || window[prefix + 'CancelRequestAnimationFrame']
    1 e# o. a; f; s    }
    7 i" _5 O5 _/ I: W+ i% W
    8 E8 z$ z; ~. v& E; K" i    // 如果当前浏览器不支持requestAnimationFrame和cancelAnimationFrame,则会退到setTimeout% g* r+ K- M9 J. I4 b$ G% U
        if (!requestAnimationFrame || !cancelAnimationFrame) {) i$ K8 D/ n& a9 P6 a& J
            requestAnimationFrame = function (callback) {8 e5 M' \3 `/ p! \5 ~8 `. n9 U% `
                const currTime = new Date().getTime()
    : C: G- f* l2 }3 a' v1 D            // 为了使setTimteout的尽可能的接近每秒60帧的效果1 h; M. w* H& [7 `) N
                const timeToCall = Math.max(0, 16 - (currTime - lastTime))' k: ^' ^. L5 a/ U  l, P! z
                const id = window.setTimeout(() => {9 X2 F) h4 r9 |3 U$ q# ^- C5 ]
                    callback(currTime + timeToCall)
    . y( w4 V. Z: h4 X: o: n4 }: `            }, timeToCall)* o; b- y% v, R. M
                lastTime = currTime + timeToCall" E& I2 g9 u( ?5 R9 \6 c
                return id
    ) y5 p) A) P. D% z4 a0 W        }
    ! K  C$ z3 {9 O' u" G' j( x
    ) P" H0 o$ D. n8 r& B: M        cancelAnimationFrame = function (id) {5 M) F9 w2 J2 ]: m
                window.clearTimeout(id)
    3 Z- J# V* N1 G! O1 A4 [. X" m0 `        }) g, a: V% m% l
        }
    & ]2 h, F! y2 K}: D: `4 j! i3 R5 Y! z3 ^

    $ [, b( s( `3 u2 y: n+ ?* M2 Gexport { requestAnimationFrame, cancelAnimationFrame }+ i* p6 [, U( u

    3 @8 \+ e" p: [- }: c8 t8 b/ G- ?8 p5 f) R/ }
    1
    & ]/ a; a3 I9 q9 c; j7 c* ^2( }3 i0 e6 ?5 v; w# [1 Q' X5 y
    3
    + L& n7 ?8 j0 y0 J" V4
      u( I$ Q+ N# |( z' m* s4 R5
    5 I" U7 p; K9 Y  R6
    ' X9 N2 L! B$ g1 C; U74 h: T6 [" T, @. r. S
    8  c% J# ~  V9 @+ o
    96 @/ P- B3 M* z7 B, H4 J/ Y
    10! h% a5 Z: j, Z/ e6 E, ]8 ?
    11
    8 r# t+ b' ]/ K% X9 f12# l$ l5 |2 @& U$ v. D
    13
    0 e+ e6 b. @, X& I* i5 M) _0 k14( o) v8 E+ j: \1 F( \5 j
    15/ f. I+ [. Z% I
    16
    " X: c  N# [* A- N! {2 Y17$ S. E" q! U9 F8 i, [  h
    18+ R$ D5 c6 e/ l" w+ B8 H5 S
    19
    ) M; ~6 I; }1 Q20
    ! A+ r2 `# l4 }21
    5 @* M8 s% p6 {, V3 V228 Z. L# V4 G, N/ w! ~6 x7 |' u# g3 `* {
    23! q) ?' |" N  |5 H9 _  B/ M
    24" K: I* {$ @8 `, ?, E
    25! ?6 ]" r" e: n( @0 H# t5 V! D5 H; F
    26
    2 U2 u& ]/ r0 V0 r27" N2 t: H. {7 h& y
    28
    ( A) Q( j0 Q$ P$ R1 M6 B29$ {* m1 d, m, Y
    30
    0 N: d. }, m2 X% P. M0 Y! J9 u8 W31) T* |, Y1 e) ~- r+ U
    32
    1 p4 |; L4 w9 G8 \0 t33: w0 }0 {7 @* s. M
    342 b# X) g2 b9 Y# v$ z
    35
    ! W/ {3 u0 q2 j7 T& l2 z9 `2 C365 h, a9 N$ f5 Y( t- Y
    376 P% Q: O- [  L2 R" _3 E- v4 {
    38
    " H6 K* D: O* {; {39
    + q; _$ w! m) w4 ^6 u8 ]; Q8 ~40
    1 i' Y  i% _3 P3 h. ^2 A41
    2 k' I% d& g9 s' U1 B) E42
    5 d' Q1 w: u* K$ U4 J$ E3 i43* c) ~9 Y1 r' D+ g5 a5 w
    44) G7 Z1 [$ F; q( `6 v8 z4 R" d2 D  u
    45
    8 F/ r2 F. _' `9 y* E6 X! g. r46
    4 w; X/ I1 U# h9 E8 ~" M7 Y( k: }0 a47
    2 A  w! \; T: P1 P# }489 n9 a4 s6 M# I9 y$ ?
    CountTo.vue组件思路
      u) X' M: g* M' E8 ^4 `& O首先引入requestAnimationFrame.js,使用requestAnimationFrame方法接受count函数,还需要格式化数字,进行正则表达式转换,返回我们想要的数据格式。' Z$ f, y; ^. `+ t: Y! @

      a1 K6 F1 F' Y( t3 B2 p0 {8 R引入 import { requestAnimationFrame, cancelAnimationFrame } from './requestAnimationFrame.js'" O* n# {- f$ |
    1
    " u% H- j0 N% v" N3 A8 U2 R需要接受的参数:8 ]  [' U" j; L; u
    9 P$ |; V9 d2 e6 U" u: f
    const props = defineProps({
    ' _& ~* A* f9 T- m1 G" e  start: {
    5 v: I( s( ?, {' n. I: p    type: Number,
    . b* J" V1 P& I! n    required: false,
    ; j' M& C4 k* @3 n    default: 02 b% s/ C1 f: s2 Y; l6 c6 X- X( n
      },* O: S7 z; {/ p3 \; i
      end: {
    4 B2 N+ \0 P  N$ x    type: Number,
    ) n( ~3 `) b7 w  G8 T% M" W    required: false,
      o$ g/ g( _: A- P/ ^. B" w    default: 0
    % o3 O: e% G- E/ `7 F, e" f  },8 U4 O9 j) g: O- d/ g
      duration: {
    * q+ p0 o) j  g( u, [    type: Number,; W' H  W: K% ]4 D
        required: false,
    ' b: h  B: Q: @2 ^& P/ g& [    default: 50002 X6 B" Y- H, D4 N+ Q$ J) h
      },, k. o2 H6 z7 t( ~4 @
      autoPlay: {' v9 I! I- [* m: Z2 f( |/ [  y, u" r
        type: Boolean,+ g. N4 ?# J0 Y6 X3 B% L( Z
        required: false,
    + K# b2 H' ~1 P    default: true9 Z% S4 Y" s, n
      },3 p. ~1 {2 S4 r
      decimals: {+ X9 \8 U% h) o. }1 {4 ~- L
        type: Number,
    3 w" w5 z5 L/ B8 R: W* x9 n    required: false,4 @- D, [2 o" H- S2 R' o6 c
        default: 0,
    - @) s9 j1 @: |7 ?+ r    validator (value) {
    3 U* l' v- j1 n, n- e; C      return value >= 00 M$ i; @7 w8 v
        }
    6 Q# d! j8 v, T* d1 a  },
    * S' f" c  [( O# I# k( `  decimal: {
    $ Z9 C; O, Y# Y+ S$ ]" n    type: String,/ w3 P5 G1 p* k" `4 A0 g# h% k
        required: false,
    3 q7 Z4 k, ~: @* u# G    default: '.'
    ) g+ E" d4 Z0 W( f) E7 }: c  },
    ( q) N6 V+ q5 O! }  separator: {* d$ S$ a- `1 h: d3 ~6 V0 g
        type: String,* Z; A# r8 m& V: J$ m$ q0 g- L
        required: false,! _0 Z9 _. D& `7 P5 [. ]/ C: l
        default: ','- i$ E* J: H, S6 U- q9 Y! J# \0 z
      },
    . U) O# E/ v5 M' G  prefix: {
    6 \4 A' f3 W# E' w+ h4 H1 h    type: String," b' |9 J# ?1 l* w
        required: false,
    , J7 h3 T" O2 M. Q    default: ''
    ; s  U. W, X5 p" I  },
    $ s  e; i6 F: V% C  suffix: {
    # V$ M! l* G1 r. D) ^- D# c    type: String,  A% ^( y3 E% T4 ?+ _
        required: false,
    : X7 z% |+ G- d$ V8 n' b6 N8 s; e    default: ''1 }9 y- A/ t% Q  p' }! i* m
      },  M5 p+ \6 A% t! E/ i
      useEasing: {
    ) n0 \5 y" q: m; Z) ]    type: Boolean,
    4 w" N4 t* ?* S    required: false,5 b- F$ u6 y5 N1 _( G7 a
        default: true
    ( Q0 k$ X4 M5 E  }- h5 x  },
    8 b. e, R9 \! [+ }  P  easingFn: {
    # P6 l( J  r, Z5 G) s! o0 ^    type: Function,5 e5 a: g* @: G. F, K* i& d
        default(t, b, c, d) {
    # p2 |* M8 S- l+ X      return c * (-Math.pow(2, -10 * t / d) + 1) * 1024 / 1023 + b;
    0 e1 U; ^0 @3 \6 F. B* B/ J# G    }
    & u+ D8 Z7 M/ a' @, z  }7 x) T; n0 ^& V5 \' o# }
    })
    1 c5 A- @# I3 Z; T% k
    5 F+ p0 I5 a, V2 S6 r/ \9 |. q3 B
    6 z1 j- ^) p# O2 M1 x( r. M! z1+ g) B) ?, s$ M% X( o
    2' n4 V6 K6 a5 z( y- M5 Q
    3
    7 E. G( A& p8 X+ t7 O: {! L49 o* w' d2 ~. D7 o0 S0 H; B% [* A& _: b
    5
    ( M. J* G* x$ z. G- b1 J8 n6
      S! u4 d) |9 ?- n7- M% r$ u, U) o0 B# ^- m- T8 d
    8
    ( R$ h0 V! N7 j1 @/ x. w7 C9
    + U1 S$ f+ V* M0 z6 ]5 Z10
      E2 \8 N& x* M7 d2 i* u! P116 O3 @% B. F/ t+ C: _
    12; J% J: K) V5 d: i3 r$ k
    13
    - H; e4 \3 ^) B6 ]9 e) w14
    : D( _1 H! G$ m3 m4 b15
    0 L! ~9 |8 D. l/ I& }16
    5 ?" b) q* s# ]! f5 I17
    " |& x3 V% c& S4 x( h3 r3 l18
    + |/ q6 k# c5 q8 v4 `19
    : V, k8 u# ^" j+ A( d% E6 T20
    / H; D" |, z% g6 `) f1 R217 j" y8 h9 [  D# R
    22
    : O5 o5 |9 j2 |$ J1 v& }3 z* w. p23
    # ~/ [0 P- a/ h8 s24% k; }$ \2 j, o8 D; ]
    25
    % {4 |! G) o1 _) q+ F3 E+ X& w26
    # }, O# f# h7 `: t  Y27$ L! Z: k7 A* C+ _
    28
    * x# @' w4 R* n( o. c  }4 x29- T# s6 \/ u* s9 U; J5 U
    30
    8 u' I: x0 E& G3 _# M. X31
    + d; V' {3 ^1 u8 p2 I1 Q324 f9 E. G+ _) [$ `. b; @. K
    33
    7 \+ R8 ^3 A4 d0 w; X5 K! w5 B346 i" _1 c) ]! P, H  c, O
    35
    $ J* a% ~0 d1 f& G" u% _36
    . x$ u4 h/ `6 l" |37
    ; s( H/ ^6 v3 V$ w2 p# n: ~38- r/ P1 ~  b; [2 y
    39
    * S3 H( W0 d% v9 q40
    % W6 Q% i! W) v9 p% o41
    - n$ b6 d9 R7 Z8 K2 q/ J7 x- j42$ ^0 e: t9 L& v8 @9 }6 b
    43; e5 ^5 J3 u  V8 h
    440 _4 f4 n. u* B
    45- [2 u6 z! t3 s: K' ?
    46+ n7 h. O5 M/ p- q
    47
    / O* D! v# V' e' K: {. }! O48( |" d( M$ `& U6 B
    49' D+ a2 ~% R. c" }
    50+ A: Z* [" }) \% A, k
    51
    : B- d( F; w5 M+ a52% L" H7 N4 }) t2 q4 |
    53
    + N2 v% N* \4 m: x. ^6 d9 ~0 o, t548 N! y. q1 w: X6 T1 S- ?
    55  ?6 u+ A: O" W" j
    56
    ! B$ h5 n4 i+ d, c8 k0 l9 c57( k5 ?  _+ B; @) j* ^. k- w4 o  y
    58
    3 l$ l) b" [, f/ y& T59
    % }) X. r5 G( d' K) j60
    " E" i' j1 c5 A4 q& i- ]! W61
    , b- @$ U$ X4 Y62# i, \, d; l+ g- c& m
    启动数字动效
    0 u( }  ]5 C. [& |9 x4 o/ L- T6 f7 w, u. u6 i6 ^
    const startCount = () => {
    + m3 L0 p) ~5 R& {( R5 [+ ?  state.localStart = props.start
    4 r  P$ {! g$ }7 U- S  state.startTime = null
    8 K; k# o- q9 r. t$ M3 S# c  state.localDuration = props.duration
    - R% t* u; h" r! \( s  state.paused = false
    2 L/ y4 Y* ^  r/ R5 P& Y: [  state.rAF = requestAnimationFrame(count)
    / R8 e9 Q/ v/ A8 r}! k# x  E" ]  @
    19 {- ?! ~# e5 c6 Y; X4 e9 D) u
    2
    " W! |2 L2 c6 N- M' R% D& K, E% L3
    ; [$ t* I0 V# m6 C! G! h4) S+ s- `' K! X- W* W) t
    5- m9 Y, x5 r3 c2 k
    6
    # S" \$ m% r% y0 g, C7+ s) z! M4 r  m8 N! j
    核心函数,对数字进行转动
    - Y1 }' v5 M4 \% b9 Y% {4 E4 F
    : u8 I7 h2 V6 T' g% M  if (!state.startTime) state.startTime = timestamp
    5 \+ V; b5 X) m" D. k# b  state.timestamp = timestamp
    6 }7 P4 [6 l" C* m  const progress = timestamp - state.startTime/ U' [# n) v1 t, |( X5 @
      state.remaining = state.localDuration - progress2 P) {: _: C, D- u
      // 是否使用速度变化曲线+ k0 f2 a$ w& C9 [
      if (props.useEasing) {
    ' z$ E) h2 @7 T" g& e+ ~; H    if (stopCount.value) {* U5 m( x' C7 a: \
          state.printVal = state.localStart - props.easingFn(progress, 0, state.localStart - props.end, state.localDuration)1 v; s& Y/ o4 N. t! {
        } else {
    ! r. m  s2 ]9 V/ }      state.printVal = props.easingFn(progress, state.localStart, props.end - state.localStart, state.localDuration)
    : V- e) p4 O/ a    }
    3 u8 ^* q# V& @8 |+ J4 E+ N  } else {
    , Q0 e! ~: o2 [# T    if (stopCount.value) {, s/ I" i$ c4 v: e" L- i; w
          state.printVal = state.localStart - ((state.localStart - props.end) * (progress / state.localDuration))
    0 ^( c8 {$ I: f- ]7 b, C    } else {5 E& V- U* y2 r2 V
          state.printVal = state.localStart + (props.end - state.localStart) * (progress / state.localDuration)1 y# d& Z4 Z  \# d$ G8 u1 K
        }& |: w% L# q7 i! D$ L
      }: e  w; y- {- \% n- `7 N* C
      if (stopCount.value) {' y% C- i( m5 k8 {
        state.printVal = state.printVal < props.end ? props.end : state.printVal
    ' |& V" V. H% u6 Z* |% M  } else {
    / H8 \# j# f# \/ R/ O( S$ a    state.printVal = state.printVal > props.end ? props.end : state.printVal
      F- s1 p4 ?/ Z9 x  }
    % w7 |0 \7 R; r1 I* \7 }% O: T- U! ?7 M$ L& S& _
      state.displayValue = formatNumber(state.printVal)5 M1 z& S+ v$ }$ q
      if (progress < state.localDuration) {
    " P4 I% W1 G0 \8 V; {3 ^5 U    state.rAF = requestAnimationFrame(count)$ Z- |' T  z) Q/ \
      } else {
    ) U1 h$ q6 S/ g! O    emits('callback')
    + `. t3 J# `, x3 W+ [+ r) n  }) K  S+ p& [% n+ |& i1 O! p
    }7 e7 R! ]- o  u" M4 n, u5 m
    + R" P! t) [$ Y6 J- `2 ~7 q/ C8 k! p

    2 Q$ C: n& V" l! V// 格式化数据,返回想要展示的数据格式# ^. E# @  f+ a( V
    const formatNumber = (val) => {; T& E# A/ T4 I% J& I2 s
      val = val.toFixed(props.default)
    ) o6 @8 v& r  r" _1 x8 u6 {  val += '', ^; d# P0 i0 o" L3 Y7 a
      const x = val.split('.')
    . N6 N( l, F4 X- }$ G6 ^& _# Y& O  let x1 = x[0]
    + u2 I/ [0 E+ ~1 K  const x2 = x.length > 1 ? props.decimal + x[1] : ''. u: a7 Z( r0 w) q5 P
      const rgx = /(\d+)(\d{3})/3 x2 `6 p4 M  ?0 O- A" G
      if (props.separator && !isNumber(props.separator)) {
    1 o5 i% J% }. |& h+ x    while (rgx.test(x1)) {: H7 m/ n1 z! z8 Q0 E, q7 }
          x1 = x1.replace(rgx, '$1' + props.separator + '$2')
    ' ]. \+ {; J7 I  M    }
    : q5 w* C1 R1 K, v  }" A3 h1 a% Z7 Z6 r
      return props.prefix + x1 + x2 + props.suffix3 Q8 ?: F$ _5 H2 s+ c) J5 r
    }6 G% Y0 w  B6 J8 |1 D9 i

    ; p7 g4 j0 k# r1 h! O, R  g+ {15 @) ~  f1 e7 k8 k) m9 p
    2* g5 z) \/ \; |8 m2 Z! ~
    3
    3 O3 a2 l# B+ @3 {" |) X2 ?4
    1 K9 V' f& v: M9 g* h: G2 v9 y# d5- P4 o( T; k" S
    6' _2 o1 M( ^+ V8 Y! E5 i
    7
    1 H8 M5 D9 d) |: ], A3 V3 P/ u# W8$ ]& O' ?: `5 w. x& _
    9
    % x0 l1 m6 ^& F$ o; X10/ H( n" I3 j) z- s/ X' I. U& f% i. U
    111 l. ?. f" \# Q2 H, v; Z4 t; N
    12
    5 ], R5 ~" ]3 |. z+ ^0 u13* d0 b; ^- N4 r
    14
    1 H0 G: Q" {& ~) x' T2 D15
    " i' x7 q5 q0 |4 W/ R: K  k" W8 `16% O9 q" Q6 M6 `8 L& Q2 Z
    172 E: F5 _" G# r# h7 h' @4 w
    18
    ; l$ m1 U, u! Q; M* K' X0 U$ S19
    ) {$ Q  r5 D( K  [2 p% |# [20
    4 v( \7 i% y8 [21
    " A9 D6 N2 ~# V  |22  G9 U% C6 b# ^  ]+ h
    23
    ( Z3 P, ~; Z9 w# O, _7 j7 t8 F& ]24
    . f! _* d3 ^7 v6 {7 ~$ o0 u25+ s  n" U! i( t3 O
    26
    ! l, q7 {3 ], ?$ e$ g27
    1 l3 }7 Z7 V* ]$ w* ^28& M5 Y: `& j. ~0 _; Y# c( h
    29$ e8 H2 i. |# C- Q
    30- l# L5 J1 b( f- W
    31
    3 X& I8 Q% h( A- K% P1 D/ `32! Q1 }, `7 q* w3 q' u7 H
    33
    5 g! \0 l# q9 |3 z$ W3 J34
    & t/ m1 H# X8 ^/ I" `7 }* B35( y' B3 c/ \# }9 M
    36
    * G$ [, `: Z% U, X37, u. V  {3 a. D% a5 H" _
    383 f6 ~6 O( P9 A4 \
    39
    . v2 f/ D) d" U, \40- D! p; J* \* l9 z) V* @
    417 B' T, `# d- j7 v
    42  Z) `1 R/ K/ N5 U0 d( l. H4 R4 V: E
    435 i9 @+ a3 ~& |. r+ d; }1 F
    44* Y. V! r& N; H7 u
    45
    - x) p0 C0 C5 L& `4 K- f468 B' d4 W" J% L! ]
    47- H* O4 Q; }' W; g& M; c. w
    485 d+ K: x: k5 N6 [( f. |  j2 a
    取消动效
    . n0 ?) b( E/ I$ F4 e6 E) X# N+ W- k
    0 f; C8 o3 \4 O& q6 m8 E// 组件销毁时取消动画
    + E/ x7 P8 `' B5 ^! s2 FonUnmounted(() => {0 N1 ~. h8 o; z. D6 C  Z* x% O
      cancelAnimationFrame(state.rAF)8 U( J4 z# s0 K: {
    })" b( B( e9 H3 ?( e* o% p# C( b; f9 x
    1
    / {' t: b- z" z" H4 z% s  ?" d. ?2
    3 ]0 b; {, w" @! [0 m1 c3 f! t3
    . p( e- g1 J, M# d4 h! I5 M; h$ f4
    $ s4 [+ b$ I4 f6 t完整代码0 B9 p! \  P8 G2 ], j2 s3 o# V4 U

    % B( A' L: [% _1 c5 }9 Y<template>
    6 @- r% L& a8 u  {{ state.displayValue }}
    6 U% k# I5 ^+ A; X0 x' {+ @# G1 C</template>
    ; N* L: V! I* [* \- r5 q+ n5 s4 x5 A5 {5 u: P& ?% }0 L
    <script setup>  // vue3.2新的语法糖, 编写代码更加简洁高效  ?3 U9 k0 p5 D; [
    import { onMounted, onUnmounted, reactive } from "@vue/runtime-core";
    4 ]( i, [  K+ w! Iimport { watch, computed } from 'vue';; o; w  ^# k  j5 q. ?( o
    import { requestAnimationFrame, cancelAnimationFrame } from './requestAnimationFrame.js'7 B5 V, Y$ ]  B3 l
    // 定义父组件传递的参数
    ; x9 x/ d/ D+ B0 [) y& Jconst props = defineProps({
    # a  B7 M* R/ z8 A5 a" ?4 W; T, n) m  start: {7 u- ]7 ?# b/ o" n
        type: Number,
    ; V$ p) Z# X0 a0 q( {5 w1 v    required: false,6 D% D- j# p" y4 D9 C9 u, t
        default: 03 |, c8 b" \3 u
      },
    # C7 ]& B6 z# @3 ?& f* t' w* M, K  end: {8 r! J5 |2 T+ T, ]' i, T, h; w* i
        type: Number,
    ! Z9 g+ l- c8 Q5 B* X$ S    required: false,
    + T7 v( C; S& v! S0 v    default: 0# l9 {2 i# Z9 y
      },+ j) j) a' ]. b2 U/ T- z+ R+ l/ f
      duration: {4 V. B1 S- P' s" _' E- T+ J1 S9 l3 A
        type: Number,
    # b& U6 F8 x( ^( X& `. e& L; Z9 z8 u4 y    required: false,; S# C" w% v& B$ c& X1 a5 G% I% g( A
        default: 50008 B# S7 ]+ D4 u. ?# u# |
      },
    3 S& Z# T- [3 S- F7 H  autoPlay: {
    " ~) T8 ~4 d- C; n; `    type: Boolean,& |' ~) T5 N% |0 \& }0 t' L
        required: false,- f& Q" P6 A: y% n0 {8 p* M+ z, t
        default: true, L. c, [& i* [8 i+ v
      },
    2 Q0 ?6 y3 r+ x4 _3 X  decimals: {, }' v$ M3 J7 n) V' E8 z
        type: Number,
    ; j, V6 a: G8 p6 P7 {& M    required: false,
    ) c, }7 Z( B$ U1 |    default: 0,  K* r% S2 j7 L- F  u  S/ Z
        validator (value) {3 X+ J( z" }; z7 B6 V0 ^7 [
          return value >= 04 U: u/ s, s- I% D1 F6 o  [
        }& t: _  A' V6 ]1 U
      },0 Y4 h" G. p( Z$ b& Q' X, {" Z
      decimal: {
    0 I+ \; M" \9 F0 U& T) [9 c/ W" A    type: String,) y' J+ U  \+ q6 Z4 r* P- e" y
        required: false,
    - W+ k( q% B6 q8 E' T    default: '.'$ x: @- C9 ?# c& i0 x, n& S
      },
    # N7 W; |; b" T  {$ B2 [8 W! j" D- @  separator: {8 K0 a( H/ K' W
        type: String,
    & n0 _1 N0 ]' N( f4 D    required: false,. v( C1 H, O4 _4 B% p- W6 ?
        default: ','
    4 h- g  h* |. i  },
    ( }  a& C8 T9 a, L  prefix: {: G5 e$ c4 c4 G4 v& o% I* L
        type: String,8 ~) Y2 G. j* z# L# W- [; x
        required: false,
    , j/ n% A) e& f- K" e2 W    default: ''  Q% k6 l+ z+ v
      },5 \" m  d9 T  e" Z
      suffix: {( h9 V4 F  @; J. X
        type: String,7 ]( [, e- f3 F3 v
        required: false,
    # |1 ?# H6 Q2 q& K3 l/ v    default: ''
    % C/ m; z+ W$ C+ H8 j  g0 t6 I) O  },
    $ m& H2 I5 O& T% ^" z% L+ ]  useEasing: {6 X3 y9 J0 O, d. i! v. `
        type: Boolean,
    & c+ D7 a. T2 a9 q$ ^# l    required: false,9 B% b2 E  A9 J! H. U# s% m
        default: true& V& t1 F5 W, J+ y/ q1 Q- m$ e  n
      },
    ) B- p0 ~; m) h  N) ~  easingFn: {
    7 ^1 i" o$ d0 H    type: Function,
    ' u( k6 h/ k, c/ G6 i, E0 ~. C    default(t, b, c, d) {
    2 T' m( J5 X2 Z! L- v  c7 ^! B$ O      return c * (-Math.pow(2, -10 * t / d) + 1) * 1024 / 1023 + b;
    . V: L# m' u) }6 f6 f4 H  X8 b    }$ D: C  b! R6 b6 c7 l  M
      }" ?: c7 Y, t& ~3 |7 n) k, ~
    })
    4 m% X3 |3 T% B( }! }1 N
    . v. g7 e$ P- M  v& w7 jconst isNumber = (val) => {
    8 P5 n0 Y$ j# ~; E$ k" m: B  return !isNaN(parseFloat(val))- Z4 T) N# w* a+ w
    }
    ( w4 T5 E) x+ l
    7 j0 w3 M+ H8 g9 {7 I  d// 格式化数据,返回想要展示的数据格式7 p4 W. L0 a4 e/ X. p' b1 |2 V
    const formatNumber = (val) => {
    ) z& D$ D6 K& f; i- J  val = val.toFixed(props.default)% M, m8 a6 t' }
      val += ''# V7 Z6 C( T  _  x
      const x = val.split('.')3 f, A# p1 D) ?
      let x1 = x[0]* b+ ~/ u& n4 o. X
      const x2 = x.length > 1 ? props.decimal + x[1] : ''2 X# [0 p9 l* R+ o6 H! l( M
      const rgx = /(\d+)(\d{3})/
    $ C5 s4 k& E! n& T  Q7 x8 j+ y  if (props.separator && !isNumber(props.separator)) {2 E5 H: w& z% r5 E$ @2 r2 `
        while (rgx.test(x1)) {! c! w* k5 o6 F3 @3 A2 U; x# h! q$ r$ o
          x1 = x1.replace(rgx, '$1' + props.separator + '$2')
    ' J2 v$ y/ g' p    }4 n  U; C( v* t2 ]& ]8 r2 d
      }, [( j6 ]0 [4 q/ w
      return props.prefix + x1 + x2 + props.suffix1 g$ a- p! y% X% L  T
    }
    " j# P# D5 g' y
    / W" L) G* d! o1 k6 x// 相当于vue2中的data中所定义的变量部分
    & U( [8 t0 @+ e/ K) N5 Jconst state = reactive({) n# m( r, H* L0 H; J, i
      localStart: props.start,
    : ~9 t& a. x4 F  displayValue: formatNumber(props.start),8 _1 W( i# f8 V, [$ Z
      printVal: null,
    1 [( X6 x8 }! u! W. T  paused: false,
    6 c, Z! e8 j9 M( K9 N0 g8 x  localDuration: props.duration,) u2 r: [2 E' Z2 u! t( r
      startTime: null,
    " T7 o5 L7 ]5 @3 K: G  timestamp: null,
    $ u- _2 Q* [, T# _& J4 `3 o; u  remaining: null,
    ; q5 }8 O+ ^  K# ~  rAF: null
    ; n% S. j, j, @& b, z})
    4 h$ ^. l3 U& s8 k  ^6 y5 t. z. M5 y$ Y
    // 定义一个计算属性,当开始数字大于结束数字时返回true! H6 Z5 c" F9 c
    const stopCount = computed(() => {- n1 s# G) V( s  z& ^
      return props.start > props.end6 S6 |; ^' k3 G/ b  s
    })
    3 A" A4 D  g( E// 定义父组件的自定义事件,子组件以触发父组件的自定义事件
    9 i9 G9 P* x% Nconst emits = defineEmits(['onMountedcallback', 'callback'])2 B$ x! y9 Z+ Q' X1 i
    4 l1 m! ?+ P# s- t& L2 h, a6 Q" U2 X
    const startCount = () => {
    % d* D: J; i  s) q/ p3 r  state.localStart = props.start- l0 \2 B: O4 _4 a( G" i
      state.startTime = null  ?+ o# o+ ?  k: _* N
      state.localDuration = props.duration
    " O  h0 Y: }( T, t" q  X0 ~  ]( `. u  state.paused = false
    " ?. w; ?+ S$ V5 y. E/ x" D- i/ W  state.rAF = requestAnimationFrame(count); \; C8 f5 _5 P5 m' F! ~: Y- x
    }6 @6 p3 d+ f+ N

    ! F# ~$ o# t  H  H7 u. Ywatch(() => props.start, () => {
    3 g5 U1 L3 z* U  if (props.autoPlay) {1 }+ S6 c" F3 o1 i- e5 n
        startCount()
    . P* [2 d! t( d, w  }
    + M3 Z6 a8 h8 [( S- `& l  B3 v})$ @3 i! x5 Z% j# K* F- Q

    ) d: O6 Q9 E. r8 b% B  M' uwatch(() => props.end, () => {3 j2 u5 Z* l$ N: }. z
      if (props.autoPlay) {
    " f" e. m5 g! Y4 Q    startCount()
    3 @& T7 P% H) @1 B* D0 N/ j. t; L  }1 Q. M! e8 H1 {% T5 n8 q
    })
    9 Z. G7 g: v% n& _// dom挂在完成后执行一些操作
      \# W* F2 G0 `" konMounted(() => {
    4 t- T3 d: g* R9 K" M6 G+ K  if (props.autoPlay) {
    ! ]# d4 u1 z/ T/ ?6 j7 _2 E    startCount(), n; _6 t& v- D  g0 S
      }
    % q$ s2 ~5 l/ ~, C* O  U' N  emits('onMountedcallback')
    7 K0 c1 ^3 W- |  R8 O9 t  t})
    3 Q9 A) p& P7 _+ L9 ]// 暂停计数( U4 w3 b8 F9 p4 p
    const pause = () => {
    5 c3 Q% Q) ^/ a' z( t5 J- u' f$ w  cancelAnimationFrame(state.rAF)
    2 k- v3 a" ]( G* h}; r; b  o( A  Q: {) Q$ c
    // 恢复计数
    " `4 d4 w! m, y8 g& }9 d- `const resume = () => {
    % B$ N9 T  I! D  state.startTime = null9 X1 x6 P' D' a! V) W' \1 |: z8 [
      state.localDuration = +state.remaining" w. }2 r# @) Q
      state.localStart = +state.printVal
    0 T6 v, |9 V( r. \+ n  V, ?8 a  requestAnimationFrame(count)* G. H' w  G6 f$ H! C6 `
    }
    6 X! }6 m  M: c. k$ w8 H9 W8 A9 r( q& v( }! @9 g
    const pauseResume = () => {7 q5 }) W6 f: u9 Q1 H* y
      if (state.paused) {
    / z% u# p9 O+ t    resume()
    / N% X# p5 E1 A- B  N3 R0 E    state.paused = false
    4 J. N8 A4 |0 E8 l) a  } else {
    ) I: o6 ]* Z3 s6 z' Y$ a! s7 [    pause(). ^  s9 X' x* ?; D
        state.paused = true8 n1 j0 p& Z" ^4 R& u& V$ D2 J
      }" t8 N- W# s6 d3 L$ D8 ?9 f- K
    }
    9 d/ X$ B( K- d
    : o6 N; n' J9 b1 Econst reset = () => {
    ' l  Y( b+ e$ R% l1 R  state.startTime = null
      p! P$ A" V3 T( E  cancelAnimationFrame(state.rAF)
    & w0 _1 V4 W. }/ {5 S% C  F2 u  state.displayValue = formatNumber(props.start)! C$ F0 t" F- t% z; L) m& Y
    }5 p2 J9 \$ J5 t% x7 |. o
    ) x' E9 r; [+ L$ J7 ^; V$ R) L
    const count = (timestamp) => {& `% K5 `% A/ w' n
      if (!state.startTime) state.startTime = timestamp. r* y) Q* `1 J. m& x; N* p
      state.timestamp = timestamp/ U5 Y2 C* X" b/ F' R9 F
      const progress = timestamp - state.startTime, @* n1 q" R8 n$ r( m; v; p
      state.remaining = state.localDuration - progress$ Z) J: x! Y0 d, U/ W7 D
      // 是否使用速度变化曲线
    3 T: B0 E) m' R$ f( ^4 I/ l  if (props.useEasing) {+ }. x. g* D0 R# N6 G. O1 N! M
        if (stopCount.value) {& d) o! Q6 p& Y6 l1 |. R" Y
          state.printVal = state.localStart - props.easingFn(progress, 0, state.localStart - props.end, state.localDuration)
    " u9 b4 t0 t/ Y- N" H( d9 i3 E    } else {
    5 _+ ?6 H1 L% ^1 l      state.printVal = props.easingFn(progress, state.localStart, props.end - state.localStart, state.localDuration)
    - Y& E% X4 @# a1 v6 J) Y2 y' x0 Z    }: [' [4 J6 D/ N
      } else {
    ( B0 \- n% ]& W& f& t    if (stopCount.value) {
    7 w% i7 F6 F9 w- H" }/ m      state.printVal = state.localStart - ((state.localStart - props.end) * (progress / state.localDuration))* i! B; B1 m. @. O- B, R  z6 V9 N4 J
        } else {
    3 D" W( e3 g5 q. ]      state.printVal = state.localStart + (props.end - state.localStart) * (progress / state.localDuration)  a' ~8 {- b1 Y) J/ k# v$ T3 T; a
        }* j% Q* Z5 b( k4 i9 z
      }
    $ @! g7 _& Q6 S0 Q) ~  if (stopCount.value) {
    % Q5 M# t- c% s8 ~) v9 Z2 G' g    state.printVal = state.printVal < props.end ? props.end : state.printVal
    " T+ V# S5 e4 l0 @9 ?# I  } else {" x4 ]# r" y1 ~8 z  ]9 H1 T
        state.printVal = state.printVal > props.end ? props.end : state.printVal  B1 g: U$ F6 j; u* S
      }7 p1 L& E5 N7 s& O2 }5 y

    + }# N" U6 J8 S2 B  state.displayValue = formatNumber(state.printVal)
    & a  \' |. h  y  W. E  if (progress < state.localDuration) {
    * z( J/ B* B  R    state.rAF = requestAnimationFrame(count)2 t! H% b1 ^. P! v" }
      } else {
    ; U: @/ }/ }! j+ f, [6 I" O3 f    emits('callback')
    8 p  P" z$ Q; ]! ]- ]& f  }
    4 \: L9 h$ ?; a4 t  X}. G( `; V( x7 c6 o9 L) x! A
    // 组件销毁时取消动画
      |$ R( N+ e& p5 e( O3 S& PonUnmounted(() => {! |0 M  z7 I, J* `
      cancelAnimationFrame(state.rAF)+ A. e  R1 ^  F" W/ w
    }): q0 B5 q2 ~& @+ a, x% p
    </script>5 p2 {; K, c& o! F% I
    3 E+ y2 M8 Q; b* n2 z
    1
    * n% r( E4 K+ G  W( y, m5 n3 W29 }/ u, x0 }4 Y
    3
    # H$ K8 Q( ~; M' K* x4+ ~1 ]3 ^% W3 X% t
    5+ u' L; }  `/ L! ]# o1 w
    6$ K  V3 ~; I& K7 l! x: A* k& J
    76 d2 o; q  }$ E  {& r9 E# \
    8
    , a- [! ^- ~$ L. Z; h% R( v0 k90 ]9 J5 r( j; b  q$ Z
    10( v- j  h8 K1 T0 x$ p# Q! ?
    11
    2 A; D1 T: K: f# c127 M; s! ?* s% G$ {/ U  G( q
    13' ^$ t/ Z! T2 j( S7 [# n' I
    14
    , C. |% u% M0 i1 i9 c156 F6 J4 R# z% p+ x* Z2 g
    16
    * N9 g0 y% _- T5 ^8 ]17* M, N, Z+ j5 w8 q9 O0 d1 J- _
    18/ q3 g" K# o! {( O: e5 B* Y1 |3 S! R
    19. ~4 G# x' H5 W; l
    20
    $ N, P; L; w5 w. [4 d# w. G# E2 X& N21
    5 l5 m7 f5 Z- n7 _220 n  r: P: \6 k0 v5 V" i
    234 w& s7 o. D: }* W9 ]. h, T' h* n
    24
    % m! A' h4 L3 {6 Q7 i255 o% y9 c8 a: V$ k) Y& M% K+ h2 s
    26
    ; j* e  ~& K4 {; N2 S8 q27
    , F" E& _& O2 g  n% c285 Q) m1 L0 J6 r+ ]! \
    29
    ; p+ t. ^$ E/ b4 m4 h30
    6 q: w2 B- W# z- b# ^8 @318 E& D) k* p: j: g
    329 M, |& \. V2 ]6 G7 Z
    33) h7 `- ?( }* R1 a' ?8 j
    349 b3 _& _  W% g
    35
    # a4 M( ?& e' h36
    ' g5 b0 P* M& z6 j! x' l37
    9 G3 K& W* h2 A% k, h5 `* B383 Q. `3 Q) s% O; U
    39
    3 p# H1 B  E$ v1 q; s1 M5 k# I40
    3 W2 d: D& a6 I1 `5 ~/ x3 ?41% I& K; z8 [5 }/ V5 J
    42
    8 \3 w! `8 V/ |8 @! g; L9 D! m435 P3 ~3 Q$ m3 _
    44
    5 R/ z, r. G. U# t* Q- V2 O" x: ^453 }. ^" a$ x" d1 z
    46
    ; Y9 i( m) ?& G9 A& l6 C47
    7 ]  g" W! c, B# Z- B: [" u48
    ' M  N" `' T1 e49
    6 D- \& c, A6 w7 l2 l( i- L9 k506 m& S2 A1 R% h+ i# w; @
    51
    ) w$ c& o+ |! {; Z5 I9 R52! a; a6 Z7 V! W% ?* ]
    53& }  w8 t6 u2 [6 z$ c5 a  X) {
    54
    & q/ R. e: N0 x55
    ( ?6 t2 R8 |/ M) @; X* V4 l! @1 u565 _) S6 L% B* K' |
    57
    7 A- P- F+ `* i- `58
    - i- I' \: Q4 V3 |* R; Y4 ]% h# x9 a59
    & }4 y1 U1 N) y- Q% m# s60
    : ^7 t- r7 [# B+ a4 l) a615 F- W8 O8 f7 T; S9 u) i
    62
    5 i6 c( H8 m3 B, j, n9 C' q/ i63$ R' L0 Q8 l2 Q0 x
    64
    0 u  k8 X3 _; ]# p65
    - T$ Q! _' n1 k" i: I% o" @- [& P66+ \3 i& c9 _3 Q% n2 x' B; ~
    67
    9 ?8 v& i% U. v0 _$ d6 X5 N683 S: Z+ j! z$ H  h/ f
    69, N6 B+ P8 e6 `$ B: ^4 x0 N
    70
    % r; l4 t- H- |6 ~715 ^- w* C+ T. _: V1 f
    724 x# M4 ^; M! i# k. Q, ~
    73! _- h) F! ^0 q; G$ s" o  j
    74
    : v' X2 a; @- g+ V6 L75' Z3 ]6 ^7 D' t9 q1 }6 E% \
    76' }# x3 n+ [2 x; J. @
    77" y' p8 W! h& f+ w2 M3 q
    78) i; x$ B& b4 F! B& S/ A& K
    794 u" z* j1 Y# S6 X* u
    80
    7 H, H3 I9 T6 L, j4 B8 K81
    7 {8 F% L. t' C! U7 s82
    $ ?) g+ Y: u) i* _835 E' H; j6 T7 x0 `5 W3 f: U
    84
    6 x; R4 j$ M, `( h6 e85
    $ n1 ^! n3 e6 j3 ^. K! @86/ |+ ~7 D4 w6 a7 y
    87
    # l3 h, _6 Z! f9 a* _* c888 s, @7 o/ t) {1 y4 Q/ Y  k
    89
    5 ~! s( c7 w7 r907 q, o# B8 Y5 }& z
    91
    " s0 }% @) e" S, h, `92
    6 n* W0 E. H$ D. X0 E( M. K: |936 q6 R5 L9 |! u1 K( S
    94
    2 A6 u" H* K3 U953 Y% M6 V/ p( Z
    966 ]% W- S9 M7 m4 `
    97
    ; a  P1 z$ J# s. Y: Y  a986 S/ b. G6 D. n' i6 t% y5 w% d
    99- t' o, d8 _. m( K/ K9 K
    1001 t8 C% p6 a# [" r
    101/ I; [# D9 |# M7 u9 P
    102
    ; W5 b9 f( j) k  e103
    + ~1 T8 ]" U/ R7 N/ W( {  n104
    * N5 Y$ S- e7 I' G105
    6 T/ ?# Y, f, d' G1 @5 x7 @5 X106
    - n  Y% Z; X9 x. l- J) i7 W1 B107
    ) l8 S5 r3 u& ^* N108
    8 d- h5 @0 x! y8 T) Z' G2 h) j" S109
    % Z: p$ o9 ^9 ?1102 v4 ]2 f9 \* _! k6 l: D0 O- w
    111
    2 v" A/ j7 |: d- @8 B  N112/ w% m, F. C% j! L  N
    113
    / {' I7 p* j6 {0 I/ D" p+ ^114
    / u/ X; e7 C( R- Z2 [5 W$ Z9 \115/ z8 K/ Y# z0 o& O1 F5 |& v
    116
    - z6 K* z" u* [# [1175 N5 Q: O0 M: [* g. n; I/ M
    1188 ^- J) U+ r  G$ r5 C+ L% d
    119
    4 H3 a+ b& B3 G; s120
    ) u, T0 A  v. w6 V+ N8 {121
    / |& n0 V& K$ K/ T( h122
    ' T5 I. g" j7 o1 R5 T" o+ B123
    - b5 e- {9 k4 D3 T( d124$ Z* D8 O( ]. l; [  c% W
    125
    0 D, J; |- }# f4 F$ v; R126- O# n4 v2 B4 X- s; r% _
    127
    ; G# t3 a# Z; z" Z8 r4 d) g- b128
    2 k2 y2 c* G7 D0 e1 n! a5 s129
    5 \7 Q, z, r$ Z8 g) c+ S& i130
    6 N4 y( z* k$ _) O4 e* A131# R, Q' t$ A' X
    132
    " Z6 A0 {7 x" W. s' v5 K: Y) q0 i% X133
    - ?8 C4 T) B1 [) J" X134
      P# Q* b; k0 _: ~* j; Y1357 {. D/ w& {" f9 Y  P
    136- D% `7 l7 f4 g3 j; h9 h
    137
    " \1 j. b  y) L5 y* U/ \138
    % L8 }+ `4 w" k' c  D. Y% \/ s139
    / Z) z# N' q! x/ d3 B. A140$ h: N+ x- ^# T, a! J% e
    141# J* R, O7 T$ Q. l
    142
    2 s, I' e. M4 {2 s1 x9 q143
    9 K! f) I/ h/ M4 z7 |: \144
    / E2 j4 I+ x; J8 A( O6 K+ ^1454 ?6 L# K* `) N6 C' B! R
    146
    2 I$ [7 i0 a( ?4 k& i6 A/ A147
    " t9 O0 m3 S" L3 g* ]; L3 y148' `0 H5 }4 D$ }" ]& m2 R
    1499 H& \- Q) P4 Z2 s
    150( g7 R& d6 J6 P9 m
    151
    4 f' ?! R1 M) M8 F( j152! S! T& G3 x1 D; k8 I! v
    153
    ' l* F1 u4 p4 B/ |' P/ D154
    / t7 e% ?1 F1 q8 w0 a+ g155( ^' n! |6 q+ |8 |' U
    156& t) k# O' S( k5 s3 k7 [
    157
    % Q9 y1 N: s% m158
    4 Y% r- S5 n) O- ^159
    0 \& s: X4 Q7 s- W: f160  ]/ s& l' W) h( }( ?  F2 U
    161& R& V. k3 I( H4 G" u( r
    162$ y6 A" a; |. i' d5 e- h
    163" q; w. u* l) K3 r& R; n
    164( R' f4 F" f6 a) D) t0 h
    165, [7 P$ d2 l4 k% M) _8 O
    166  W9 |+ X5 c$ z9 ^7 ~# V
    167
    + c' Z: I6 R& M. E* h7 r168* C5 m; F& K. n* D6 L+ S9 r+ S
    169
    4 g" ^1 N* s* ^8 N" |1703 y; j/ t/ O7 V/ S$ ?) Y5 f1 ?- p
    171# J; l9 v5 [4 q& V2 Y" ?2 G
    172- d* K" S: B( {- t# V
    173
    4 p& |4 H( m( Y" G: ?174, w; `: V  w! p/ q' w6 i# U
    175: g8 @; Y9 g, S3 x) |$ W
    176
    4 \5 ?2 [/ j" _% F/ S177
    : I: r+ A3 I! I9 R, ~1 ]178
    3 n  }; Z, o! M4 h! A3 G' I179$ B9 S6 n" H2 j$ y* `* P) [
    180& t' k- [8 x; Q$ Q2 B
    181# o8 v' N" B. j3 m6 d0 \
    1829 V; R) n( g( V1 d4 @6 ?. ^3 I
    183
    ; U5 A' Q* G% Z. D184
    : h0 q- e; X: y( L  E2 L, E185
    ( o# F0 O1 m# M1 S2 ~; r% R1863 j" ]5 K9 W/ `, Y
    1876 N/ s$ }7 z2 S5 ^6 K
    188
    $ Y% c2 t3 y  C8 t1895 N, h7 H# V' O0 [7 I: Z
    190
    7 D! j  Z0 t, y1 p& U/ \1911 a, r3 {7 Q, u9 S) Z
    192
    * L7 J0 l& }$ |193
    - Y+ I) \4 `5 j! R3 ~194
    6 _8 v6 ?0 T! i; [195
    - P3 B" f% _0 i3 o; e2 h$ s196; U& f. z# V3 k1 g( Y9 F/ j- J6 L0 t
    197: R% v; n8 P; d  \9 j; Y8 B3 a  B
    198
    3 D) [/ q, f/ w199
    # \; l) @4 t& Z8 U6 K) x200
    $ b# X: ^$ @8 ?% H0 G201& a/ \/ u9 W- S! g! X7 K  _2 ]% e
    202) Q2 Z# h. z( @1 t2 ~9 m; O
    总结2 s; ?! R# l' `1 }3 j( {/ e: W- W) ?
    自己封装数字动态效果需要注意各个浏览器直接的差异,手动pollyfill,暴露出去的props参数需要有默认值,数据的格式化可以才有正则表达式的方式,组件的驱动必须是数据变化,根据数据来驱动页面渲染,防止页面出现卡顿,不要强行操作dom,引入的组件可以全局配置,后续组件可以服用,码字不易,请各位看官大佬多多支持,一键三连了~❤️❤️❤️8 S+ z; b" l7 B. l

    5 O6 w: v# p2 R$ vdemo演示
      C. R  ]( \# e$ N* C后续的线上demo演示会放在; u( {! a0 ?) R; L/ E* {; o# E
    demo演示
    3 _4 b( f5 v$ U. E. G8 `完整代码会放在
    , A: ?' ?8 q3 R( a% U  U个人主页0 Z. W4 t3 P; A
    " ]. a3 l1 t* H( W* m
    希望对vue开发者有所帮助~
    $ k5 _& [3 |" n/ d( o4 [9 x* c5 V' r! n
    个人简介:承吾
    3 M8 V8 F3 o6 h% ?, H6 Z! H9 J. ?工作年限:5年前端
    ; @2 Y9 B4 X! Y* t地区:上海5 X  h* A1 i& M1 O* J
    个人宣言:立志出好文,传播我所会的,有好东西就及时与大家共享!
    7 w0 X8 G( u8 m. ^& z5 F
    " f8 ~: M6 R0 r- r; W) i, s. |# g+ e  z; x3 e
    ( a; d% c% L% v1 T0 @- F. J$ ^

    - n2 n4 u9 ]9 L————————————————
    8 c+ X5 L2 [/ V  ~版权声明:本文为CSDN博主「KinHKin(五年前端)」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    * S9 e5 L! z4 T3 P0 `% V9 S原文链接:https://blog.csdn.net/weixin_42974827/article/details/126831847) x" W6 _) i% L  y! H

    . G/ |- M' X$ I% y3 _* r1 d: v8 z9 A: K$ B2 a! @% d
    zan
    转播转播0 分享淘帖0 分享分享0 收藏收藏0 支持支持0 反对反对0 微信微信
    您需要登录后才可以回帖 登录 | 注册地址

    qq
    收缩
    • 电话咨询

    • 04714969085
    fastpost

    关于我们| 联系我们| 诚征英才| 对外合作| 产品服务| QQ

    手机版|Archiver| |繁體中文 手机客户端  

    蒙公网安备 15010502000194号

    Powered by Discuz! X2.5   © 2001-2013 数学建模网-数学中国 ( 蒙ICP备14002410号-3 蒙BBS备-0002号 )     论坛法律顾问:王兆丰

    GMT+8, 2026-4-20 06:01 , Processed in 0.330992 second(s), 50 queries .

    回顶部