index.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. class CountUp {
  2. constructor(startVal, endVal, decimals, duration, options = {}, page = getCurrentPages()[getCurrentPages().length - 1]) {
  3. Object.assign(this, {
  4. page,
  5. startVal,
  6. endVal,
  7. decimals,
  8. duration,
  9. options,
  10. })
  11. this.__init()
  12. }
  13. /**
  14. * 初始化
  15. */
  16. __init() {
  17. this.setData = this.page.setData.bind(this.page)
  18. this.lastTime = 0
  19. // merge options
  20. this.mergeOptions(this.options)
  21. this.startVal = Number(this.startVal)
  22. this.cacheVal = this.startVal
  23. this.endVal = Number(this.endVal)
  24. this.countDown = (this.startVal > this.endVal)
  25. this.frameVal = this.startVal
  26. this.decimals = Math.max(0, this.decimals || 0)
  27. this.dec = Math.pow(10, this.decimals)
  28. this.duration = Number(this.duration) * 1000 || 2000
  29. // format startVal on initialization
  30. this.printValue(this.formattingFn(this.startVal))
  31. }
  32. /**
  33. * 默认参数
  34. */
  35. setDefaultOptions() {
  36. return {
  37. useEasing: true, // toggle easing
  38. useGrouping: true, // 1,000,000 vs 1000000
  39. separator: ',', // character to use as a separator
  40. decimal: '.', // character to use as a decimal
  41. easingFn: null, // optional custom easing closure function, default is Robert Penner's easeOutExpo
  42. formattingFn: null, // optional custom formatting function, default is this.formatNumber below
  43. printValue(value) {}, // printValue
  44. }
  45. }
  46. /**
  47. * 合并参数
  48. */
  49. mergeOptions(options) {
  50. const defaultOptions = this.setDefaultOptions()
  51. // extend default options with passed options object
  52. for (let key in defaultOptions) {
  53. if (defaultOptions.hasOwnProperty(key)) {
  54. this.options[key] = typeof options[key] !== 'undefined' ? options[key] : defaultOptions[key]
  55. if (typeof this.options[key] === 'function') {
  56. this.options[key] = this.options[key].bind(this)
  57. }
  58. }
  59. }
  60. if (this.options.separator === '') { this.options.useGrouping = !1 }
  61. if (!this.options.prefix) this.options.prefix = ''
  62. if (!this.options.suffix) this.options.suffix = ''
  63. this.easingFn = this.options.easingFn ? this.options.easingFn : this.easeOutExpo
  64. this.formattingFn = this.options.formattingFn ? this.options.formattingFn : this.formatNumber
  65. this.printValue = this.options.printValue ? this.options.printValue : function() {}
  66. }
  67. /**
  68. * 创建定时器
  69. */
  70. requestAnimationFrame(callback) {
  71. let currTime = new Date().getTime()
  72. let timeToCall = Math.max(0, 16 - (currTime - this.lastTime))
  73. let timeout = setTimeout(() => {
  74. callback.bind(this)(currTime + timeToCall)
  75. }, timeToCall)
  76. this.lastTime = currTime + timeToCall
  77. return timeout
  78. }
  79. /**
  80. * 清空定时器
  81. */
  82. cancelAnimationFrame(timeout) {
  83. clearTimeout(timeout)
  84. }
  85. /**
  86. * 格式化数字
  87. */
  88. formatNumber(nStr) {
  89. nStr = nStr.toFixed(this.decimals)
  90. nStr += ''
  91. let x, x1, x2, rgx
  92. x = nStr.split('.')
  93. x1 = x[0]
  94. x2 = x.length > 1 ? this.options.decimal + x[1] : ''
  95. rgx = /(\d+)(\d{3})/
  96. if (this.options.useGrouping) {
  97. while (rgx.test(x1)) {
  98. x1 = x1.replace(rgx, '$1' + this.options.separator + '$2')
  99. }
  100. }
  101. return this.options.prefix + x1 + x2 + this.options.suffix
  102. }
  103. /**
  104. * 过渡效果
  105. */
  106. easeOutExpo(t, b, c, d) {
  107. return c * (-Math.pow(2, -10 * t / d) + 1) * 1024 / 1023 + b
  108. }
  109. /**
  110. * 计数函数
  111. */
  112. count(timestamp) {
  113. if (!this.startTime) { this.startTime = timestamp }
  114. this.timestamp = timestamp
  115. const progress = timestamp - this.startTime
  116. this.remaining = this.duration - progress
  117. // to ease or not to ease
  118. if (this.options.useEasing) {
  119. if (this.countDown) {
  120. this.frameVal = this.startVal - this.easingFn(progress, 0, this.startVal - this.endVal, this.duration)
  121. } else {
  122. this.frameVal = this.easingFn(progress, this.startVal, this.endVal - this.startVal, this.duration)
  123. }
  124. } else {
  125. if (this.countDown) {
  126. this.frameVal = this.startVal - ((this.startVal - this.endVal) * (progress / this.duration))
  127. } else {
  128. this.frameVal = this.startVal + (this.endVal - this.startVal) * (progress / this.duration)
  129. }
  130. }
  131. // don't go past endVal since progress can exceed duration in the last frame
  132. if (this.countDown) {
  133. this.frameVal = (this.frameVal < this.endVal) ? this.endVal : this.frameVal
  134. } else {
  135. this.frameVal = (this.frameVal > this.endVal) ? this.endVal : this.frameVal
  136. }
  137. // decimal
  138. this.frameVal = Math.round(this.frameVal * this.dec) / this.dec
  139. // format and print value
  140. this.printValue(this.formattingFn(this.frameVal))
  141. // whether to continue
  142. if (progress < this.duration) {
  143. this.rAF = this.requestAnimationFrame(this.count)
  144. } else {
  145. if (this.callback) { this.callback() }
  146. }
  147. }
  148. /**
  149. * 启动计数器
  150. */
  151. start(callback) {
  152. this.callback = callback
  153. this.rAF = this.requestAnimationFrame(this.count)
  154. return !1
  155. }
  156. /**
  157. * 停止计数器
  158. */
  159. pauseResume() {
  160. if (!this.paused) {
  161. this.paused = !0
  162. this.cancelAnimationFrame(this.rAF)
  163. } else {
  164. this.paused = !1
  165. delete this.startTime
  166. this.duration = this.remaining
  167. this.startVal = this.frameVal
  168. this.requestAnimationFrame(this.count)
  169. }
  170. }
  171. /**
  172. * 重置计数器
  173. */
  174. reset() {
  175. this.paused = !1
  176. delete this.startTime
  177. this.startVal = this.cacheVal
  178. this.cancelAnimationFrame(this.rAF)
  179. this.printValue(this.formattingFn(this.startVal))
  180. }
  181. /**
  182. * 更新计数器
  183. */
  184. update(newEndVal) {
  185. this.cancelAnimationFrame(this.rAF)
  186. this.paused = !1
  187. delete this.startTime
  188. this.startVal = this.frameVal
  189. this.endVal = Number(newEndVal)
  190. this.countDown = (this.startVal > this.endVal)
  191. this.rAF = this.requestAnimationFrame(this.count)
  192. }
  193. }
  194. export default CountUp