Nav apraksta

countUp.js 7.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. /*
  2. countUp.js
  3. by @inorganik
  4. */
  5. // target = id of html element or var of previously selected html element where counting occurs
  6. // startVal = the value you want to begin at
  7. // endVal = the value you want to arrive at
  8. // decimals = number of decimal places, default 0
  9. // duration = duration of animation in seconds, default 2
  10. // options = optional object of options (see below)
  11. var CountUp = function(target, startVal, endVal, decimals, duration, options) {
  12. var self = this;
  13. self.version = function () { return '1.9.1'; };
  14. // default options
  15. self.options = {
  16. useEasing: true, // toggle easing
  17. useGrouping: true, // 1,000,000 vs 1000000
  18. separator: ',', // character to use as a separator
  19. decimal: '.', // character to use as a decimal
  20. easingFn: easeOutExpo, // optional custom easing function, default is Robert Penner's easeOutExpo
  21. formattingFn: formatNumber, // optional custom formatting function, default is formatNumber above
  22. prefix: '', // optional text before the result
  23. suffix: '', // optional text after the result
  24. numerals: [] // optionally pass an array of custom numerals for 0-9
  25. };
  26. // extend default options with passed options object
  27. if (options && typeof options === 'object') {
  28. for (var key in self.options) {
  29. if (options.hasOwnProperty(key) && options[key] !== null) {
  30. self.options[key] = options[key];
  31. }
  32. }
  33. }
  34. if (self.options.separator === '') self.options.useGrouping = false;
  35. // make sure requestAnimationFrame and cancelAnimationFrame are defined
  36. // polyfill for browsers without native support
  37. // by Opera engineer Erik Möller
  38. var lastTime = 0;
  39. var vendors = ['webkit', 'moz', 'ms', 'o'];
  40. for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
  41. window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
  42. window.cancelAnimationFrame =
  43. window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
  44. }
  45. if (!window.requestAnimationFrame) {
  46. window.requestAnimationFrame = function(callback, element) {
  47. var currTime = new Date().getTime();
  48. var timeToCall = Math.max(0, 16 - (currTime - lastTime));
  49. var id = window.setTimeout(function() { callback(currTime + timeToCall); },
  50. timeToCall);
  51. lastTime = currTime + timeToCall;
  52. return id;
  53. };
  54. }
  55. if (!window.cancelAnimationFrame) {
  56. window.cancelAnimationFrame = function(id) {
  57. clearTimeout(id);
  58. };
  59. }
  60. function formatNumber(num) {
  61. num = num.toFixed(self.decimals);
  62. num += '';
  63. var x, x1, x2, rgx;
  64. x = num.split('.');
  65. x1 = x[0];
  66. x2 = x.length > 1 ? self.options.decimal + x[1] : '';
  67. rgx = /(\d+)(\d{3})/;
  68. if (self.options.useGrouping) {
  69. while (rgx.test(x1)) {
  70. x1 = x1.replace(rgx, '$1' + self.options.separator + '$2');
  71. }
  72. }
  73. // optional numeral substitution
  74. if (self.options.numerals.length) {
  75. x1 = x1.replace(/[0-9]/g, function(w) {
  76. return self.options.numerals[+w];
  77. })
  78. x2 = x2.replace(/[0-9]/g, function(w) {
  79. return self.options.numerals[+w];
  80. })
  81. }
  82. return self.options.prefix + x1 + x2 + self.options.suffix;
  83. }
  84. // Robert Penner's easeOutExpo
  85. function easeOutExpo(t, b, c, d) {
  86. return c * (-Math.pow(2, -10 * t / d) + 1) * 1024 / 1023 + b;
  87. }
  88. function ensureNumber(n) {
  89. return (typeof n === 'number' && !isNaN(n));
  90. }
  91. self.initialize = function() {
  92. if (self.initialized) return true;
  93. self.error = '';
  94. self.d = (typeof target === 'string') ? document.getElementById(target) : target;
  95. if (!self.d) {
  96. self.error = '[CountUp] target is null or undefined'
  97. return false;
  98. }
  99. self.startVal = Number(startVal);
  100. self.endVal = Number(endVal);
  101. // error checks
  102. if (ensureNumber(self.startVal) && ensureNumber(self.endVal)) {
  103. self.decimals = Math.max(0, decimals || 0);
  104. self.dec = Math.pow(10, self.decimals);
  105. self.duration = Number(duration) * 1000 || 2000;
  106. self.countDown = (self.startVal > self.endVal);
  107. self.frameVal = self.startVal;
  108. self.initialized = true;
  109. return true;
  110. }
  111. else {
  112. self.error = '[CountUp] startVal ('+startVal+') or endVal ('+endVal+') is not a number';
  113. return false;
  114. }
  115. };
  116. // Print value to target
  117. self.printValue = function(value) {
  118. var result = self.options.formattingFn(value);
  119. if (self.d.tagName === 'INPUT') {
  120. this.d.value = result;
  121. }
  122. else if (self.d.tagName === 'text' || self.d.tagName === 'tspan') {
  123. this.d.textContent = result;
  124. }
  125. else {
  126. this.d.innerHTML = result;
  127. }
  128. };
  129. self.count = function(timestamp) {
  130. if (!self.startTime) { self.startTime = timestamp; }
  131. self.timestamp = timestamp;
  132. var progress = timestamp - self.startTime;
  133. self.remaining = self.duration - progress;
  134. // to ease or not to ease
  135. if (self.options.useEasing) {
  136. if (self.countDown) {
  137. self.frameVal = self.startVal - self.options.easingFn(progress, 0, self.startVal - self.endVal, self.duration);
  138. } else {
  139. self.frameVal = self.options.easingFn(progress, self.startVal, self.endVal - self.startVal, self.duration);
  140. }
  141. } else {
  142. if (self.countDown) {
  143. self.frameVal = self.startVal - ((self.startVal - self.endVal) * (progress / self.duration));
  144. } else {
  145. self.frameVal = self.startVal + (self.endVal - self.startVal) * (progress / self.duration);
  146. }
  147. }
  148. // don't go past endVal since progress can exceed duration in the last frame
  149. if (self.countDown) {
  150. self.frameVal = (self.frameVal < self.endVal) ? self.endVal : self.frameVal;
  151. } else {
  152. self.frameVal = (self.frameVal > self.endVal) ? self.endVal : self.frameVal;
  153. }
  154. // decimal
  155. self.frameVal = Math.round(self.frameVal*self.dec)/self.dec;
  156. // format and print value
  157. self.printValue(self.frameVal);
  158. // whether to continue
  159. if (progress < self.duration) {
  160. self.rAF = requestAnimationFrame(self.count);
  161. } else {
  162. if (self.callback) self.callback();
  163. }
  164. };
  165. // start your animation
  166. self.start = function(callback) {
  167. if (!self.initialize()) return;
  168. self.callback = callback;
  169. self.rAF = requestAnimationFrame(self.count);
  170. };
  171. // toggles pause/resume animation
  172. self.pauseResume = function() {
  173. if (!self.paused) {
  174. self.paused = true;
  175. cancelAnimationFrame(self.rAF);
  176. } else {
  177. self.paused = false;
  178. delete self.startTime;
  179. self.duration = self.remaining;
  180. self.startVal = self.frameVal;
  181. requestAnimationFrame(self.count);
  182. }
  183. };
  184. // reset to startVal so animation can be run again
  185. self.reset = function() {
  186. self.paused = false;
  187. delete self.startTime;
  188. self.initialized = false;
  189. if (self.initialize()) {
  190. cancelAnimationFrame(self.rAF);
  191. self.printValue(self.startVal);
  192. }
  193. };
  194. // pass a new endVal and start animation
  195. self.update = function (newEndVal) {
  196. if (!self.initialize()) return;
  197. newEndVal = Number(newEndVal);
  198. if (!ensureNumber(newEndVal)) {
  199. self.error = '[CountUp] update() - new endVal is not a number: '+newEndVal;
  200. return;
  201. }
  202. self.error = '';
  203. // if (newEndVal === self.frameVal) return;
  204. cancelAnimationFrame(self.rAF);
  205. self.paused = false;
  206. delete self.startTime;
  207. self.startVal = self.frameVal;
  208. self.endVal = newEndVal;
  209. self.countDown = (self.startVal > self.endVal);
  210. self.rAF = requestAnimationFrame(self.count);
  211. };
  212. // format startVal on initialization
  213. if (self.initialize()) self.printValue(self.startVal);
  214. };