count-to.php 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. <script>
  2. let lastTime = 0
  3. const prefixes = 'webkit moz ms o'.split(' ') // 各浏览器前缀
  4. let requestAnimationFrame
  5. let cancelAnimationFrame
  6. const isServer = typeof window === 'undefined'
  7. if (isServer) {
  8. requestAnimationFrame = function() {
  9. return
  10. }
  11. cancelAnimationFrame = function() {
  12. return
  13. }
  14. } else {
  15. requestAnimationFrame = window.requestAnimationFrame
  16. cancelAnimationFrame = window.cancelAnimationFrame
  17. let prefix
  18. // 通过遍历各浏览器前缀,来得到requestAnimationFrame和cancelAnimationFrame在当前浏览器的实现形式
  19. for (let i = 0; i < prefixes.length; i++) {
  20. if (requestAnimationFrame && cancelAnimationFrame) {
  21. break
  22. }
  23. prefix = prefixes[i]
  24. requestAnimationFrame = requestAnimationFrame || window[prefix + 'RequestAnimationFrame']
  25. cancelAnimationFrame = cancelAnimationFrame || window[prefix + 'CancelAnimationFrame'] || window[prefix + 'CancelRequestAnimationFrame']
  26. }
  27. // 如果当前浏览器不支持requestAnimationFrame和cancelAnimationFrame,则会退到setTimeout
  28. if (!requestAnimationFrame || !cancelAnimationFrame) {
  29. requestAnimationFrame = function(callback) {
  30. const currTime = new Date().getTime()
  31. // 为了使setTimteout的尽可能的接近每秒60帧的效果
  32. const timeToCall = Math.max(0, 16 - (currTime - lastTime))
  33. const id = window.setTimeout(() => {
  34. callback(currTime + timeToCall)
  35. }, timeToCall)
  36. lastTime = currTime + timeToCall
  37. return id
  38. }
  39. cancelAnimationFrame = function(id) {
  40. window.clearTimeout(id)
  41. }
  42. }
  43. }
  44. </script>
  45. <template id="count-to">
  46. <div>
  47. <span style="display: inline-block;" :style="countStyle" v-for="(item,index) in String(displayValue).length" :key="index">
  48. {{displayValue[index]}}
  49. <!-- displayValue -->
  50. </span>
  51. </div>
  52. </template>
  53. <script>
  54. Vue.component('count-to', {
  55. name: "count-to",
  56. template: '#count-to',
  57. props: {
  58. startVal: {
  59. type: Number,
  60. required: false,
  61. default: 0
  62. },
  63. endVal: {
  64. type: Number,
  65. required: false,
  66. default: 2017
  67. },
  68. duration: {
  69. type: Number,
  70. required: false,
  71. default: 3000
  72. },
  73. autoplay: {
  74. type: Boolean,
  75. required: false,
  76. default: true
  77. },
  78. decimals: {
  79. type: Number,
  80. required: false,
  81. default: 0,
  82. validator(value) {
  83. return value >= 0
  84. }
  85. },
  86. decimal: {
  87. type: String,
  88. required: false,
  89. default: '.'
  90. },
  91. separator: {
  92. type: String,
  93. required: false,
  94. default: ','
  95. },
  96. prefix: {
  97. type: String,
  98. required: false,
  99. default: ''
  100. },
  101. suffix: {
  102. type: String,
  103. required: false,
  104. default: ''
  105. },
  106. useEasing: {
  107. type: Boolean,
  108. required: false,
  109. default: true
  110. },
  111. easingFn: {
  112. type: Function,
  113. default (t, b, c, d) {
  114. return c * (-Math.pow(2, -10 * t / d) + 1) * 1024 / 1023 + b;
  115. }
  116. },
  117. countStyle: {
  118. type: Function,
  119. default () {
  120. return {};
  121. }
  122. }
  123. },
  124. data() {
  125. return {
  126. localStartVal: this.startVal,
  127. displayValue: this.formatNumber(this.startVal),
  128. printVal: null,
  129. paused: false,
  130. localDuration: this.duration,
  131. startTime: null,
  132. timestamp: null,
  133. remaining: null,
  134. rAF: null
  135. };
  136. },
  137. computed: {
  138. countDown() {
  139. return this.startVal > this.endVal
  140. }
  141. },
  142. watch: {
  143. startVal() {
  144. if (this.autoplay) {
  145. this.start();
  146. }
  147. },
  148. endVal() {
  149. if (this.autoplay) {
  150. this.start();
  151. }
  152. }
  153. },
  154. mounted() {
  155. if (this.autoplay) {
  156. this.start();
  157. }
  158. this.$emit('mountedCallback')
  159. },
  160. methods: {
  161. start() {
  162. this.localStartVal = this.startVal;
  163. this.startTime = null;
  164. this.localDuration = this.duration;
  165. this.paused = false;
  166. this.rAF = requestAnimationFrame(this.count);
  167. },
  168. pauseResume() {
  169. if (this.paused) {
  170. this.resume();
  171. this.paused = false;
  172. } else {
  173. this.pause();
  174. this.paused = true;
  175. }
  176. },
  177. pause() {
  178. cancelAnimationFrame(this.rAF);
  179. },
  180. resume() {
  181. this.startTime = null;
  182. this.localDuration = +this.remaining;
  183. this.localStartVal = +this.printVal;
  184. requestAnimationFrame(this.count);
  185. },
  186. reset() {
  187. this.startTime = null;
  188. cancelAnimationFrame(this.rAF);
  189. this.displayValue = this.formatNumber(this.startVal);
  190. },
  191. count(timestamp) {
  192. if (!this.startTime) this.startTime = timestamp;
  193. this.timestamp = timestamp;
  194. const progress = timestamp - this.startTime;
  195. this.remaining = this.localDuration - progress;
  196. if (this.useEasing) {
  197. if (this.countDown) {
  198. this.printVal = this.localStartVal - this.easingFn(progress, 0, this.localStartVal - this.endVal, this.localDuration)
  199. } else {
  200. this.printVal = this.easingFn(progress, this.localStartVal, this.endVal - this.localStartVal, this.localDuration);
  201. }
  202. } else {
  203. if (this.countDown) {
  204. this.printVal = this.localStartVal - ((this.localStartVal - this.endVal) * (progress / this.localDuration));
  205. } else {
  206. this.printVal = this.localStartVal + (this.endVal - this.localStartVal) * (progress / this.localDuration);
  207. }
  208. }
  209. if (this.countDown) {
  210. this.printVal = this.printVal < this.endVal ? this.endVal : this.printVal;
  211. } else {
  212. this.printVal = this.printVal > this.endVal ? this.endVal : this.printVal;
  213. }
  214. this.displayValue = this.formatNumber(this.printVal)
  215. if (progress < this.localDuration) {
  216. this.rAF = requestAnimationFrame(this.count);
  217. } else {
  218. this.$emit('callback');
  219. }
  220. },
  221. isNumber(val) {
  222. return !isNaN(parseFloat(val))
  223. },
  224. formatNumber(num) {
  225. num = num.toFixed(this.decimals);
  226. num += '';
  227. const x = num.split('.');
  228. let x1 = x[0];
  229. const x2 = x.length > 1 ? this.decimal + x[1] : '';
  230. const rgx = /(\d+)(\d{3})/;
  231. if (this.separator && !this.isNumber(this.separator)) {
  232. while (rgx.test(x1)) {
  233. x1 = x1.replace(rgx, '$1' + this.separator + '$2');
  234. }
  235. }
  236. return this.prefix + x1 + x2 + this.suffix;
  237. }
  238. },
  239. destroyed() {
  240. cancelAnimationFrame(this.rAF)
  241. }
  242. });
  243. </script>