countUp.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. var __assign = (this && this.__assign) || function () {
  2. __assign = Object.assign || function(t) {
  3. for (var s, i = 1, n = arguments.length; i < n; i++) {
  4. s = arguments[i];
  5. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
  6. t[p] = s[p];
  7. }
  8. return t;
  9. };
  10. return __assign.apply(this, arguments);
  11. };
  12. // playground: stackblitz.com/edit/countup-typescript
  13. var CountUp = /** @class */ (function () {
  14. function CountUp(target, endVal, options) {
  15. var _this = this;
  16. this.target = target;
  17. this.endVal = endVal;
  18. this.options = options;
  19. this.version = '2.0.7';
  20. this.defaults = {
  21. startVal: 0,
  22. decimalPlaces: 0,
  23. duration: 2,
  24. useEasing: true,
  25. useGrouping: true,
  26. smartEasingThreshold: 999,
  27. smartEasingAmount: 333,
  28. separator: ',',
  29. decimal: '.',
  30. prefix: '',
  31. suffix: ''
  32. };
  33. this.finalEndVal = null; // for smart easing
  34. this.useEasing = true;
  35. this.countDown = false;
  36. this.error = '';
  37. this.startVal = 0;
  38. this.paused = true;
  39. this.count = function (timestamp) {
  40. if (!_this.startTime) {
  41. _this.startTime = timestamp;
  42. }
  43. var progress = timestamp - _this.startTime;
  44. _this.remaining = _this.duration - progress;
  45. // to ease or not to ease
  46. if (_this.useEasing) {
  47. if (_this.countDown) {
  48. _this.frameVal = _this.startVal - _this.easingFn(progress, 0, _this.startVal - _this.endVal, _this.duration);
  49. }
  50. else {
  51. _this.frameVal = _this.easingFn(progress, _this.startVal, _this.endVal - _this.startVal, _this.duration);
  52. }
  53. }
  54. else {
  55. if (_this.countDown) {
  56. _this.frameVal = _this.startVal - ((_this.startVal - _this.endVal) * (progress / _this.duration));
  57. }
  58. else {
  59. _this.frameVal = _this.startVal + (_this.endVal - _this.startVal) * (progress / _this.duration);
  60. }
  61. }
  62. // don't go past endVal since progress can exceed duration in the last frame
  63. if (_this.countDown) {
  64. _this.frameVal = (_this.frameVal < _this.endVal) ? _this.endVal : _this.frameVal;
  65. }
  66. else {
  67. _this.frameVal = (_this.frameVal > _this.endVal) ? _this.endVal : _this.frameVal;
  68. }
  69. // decimal
  70. _this.frameVal = Number(_this.frameVal.toFixed(_this.options.decimalPlaces));
  71. // format and print value
  72. _this.printValue(_this.frameVal);
  73. // whether to continue
  74. if (progress < _this.duration) {
  75. _this.rAF = requestAnimationFrame(_this.count);
  76. }
  77. else if (_this.finalEndVal !== null) {
  78. // smart easing
  79. _this.update(_this.finalEndVal);
  80. }
  81. else {
  82. if (_this.callback) {
  83. _this.callback();
  84. }
  85. }
  86. };
  87. // default format and easing functions
  88. this.formatNumber = function (num) {
  89. var neg = (num < 0) ? '-' : '';
  90. var result, x, x1, x2, x3;
  91. result = Math.abs(num).toFixed(_this.options.decimalPlaces);
  92. result += '';
  93. x = result.split('.');
  94. x1 = x[0];
  95. x2 = x.length > 1 ? _this.options.decimal + x[1] : '';
  96. if (_this.options.useGrouping) {
  97. x3 = '';
  98. for (var i = 0, len = x1.length; i < len; ++i) {
  99. if (i !== 0 && (i % 3) === 0) {
  100. x3 = _this.options.separator + x3;
  101. }
  102. x3 = x1[len - i - 1] + x3;
  103. }
  104. x1 = x3;
  105. }
  106. // optional numeral substitution
  107. if (_this.options.numerals && _this.options.numerals.length) {
  108. x1 = x1.replace(/[0-9]/g, function (w) { return _this.options.numerals[+w]; });
  109. x2 = x2.replace(/[0-9]/g, function (w) { return _this.options.numerals[+w]; });
  110. }
  111. return neg + _this.options.prefix + x1 + x2 + _this.options.suffix;
  112. };
  113. this.easeOutExpo = function (t, b, c, d) {
  114. return c * (-Math.pow(2, -10 * t / d) + 1) * 1024 / 1023 + b;
  115. };
  116. this.options = __assign(__assign({}, this.defaults), options);
  117. this.formattingFn = (this.options.formattingFn) ?
  118. this.options.formattingFn : this.formatNumber;
  119. this.easingFn = (this.options.easingFn) ?
  120. this.options.easingFn : this.easeOutExpo;
  121. this.startVal = this.validateValue(this.options.startVal);
  122. this.frameVal = this.startVal;
  123. this.endVal = this.validateValue(endVal);
  124. this.options.decimalPlaces = Math.max(0 || this.options.decimalPlaces);
  125. this.resetDuration();
  126. this.options.separator = String(this.options.separator);
  127. this.useEasing = this.options.useEasing;
  128. if (this.options.separator === '') {
  129. this.options.useGrouping = false;
  130. }
  131. this.el = (typeof target === 'string') ? document.getElementById(target) : target;
  132. if (this.el) {
  133. this.printValue(this.startVal);
  134. }
  135. else {
  136. this.error = '[CountUp] target is null or undefined';
  137. }
  138. }
  139. // determines where easing starts and whether to count down or up
  140. CountUp.prototype.determineDirectionAndSmartEasing = function () {
  141. var end = (this.finalEndVal) ? this.finalEndVal : this.endVal;
  142. this.countDown = (this.startVal > end);
  143. var animateAmount = end - this.startVal;
  144. if (Math.abs(animateAmount) > this.options.smartEasingThreshold) {
  145. this.finalEndVal = end;
  146. var up = (this.countDown) ? 1 : -1;
  147. this.endVal = end + (up * this.options.smartEasingAmount);
  148. this.duration = this.duration / 2;
  149. }
  150. else {
  151. this.endVal = end;
  152. this.finalEndVal = null;
  153. }
  154. if (this.finalEndVal) {
  155. this.useEasing = false;
  156. }
  157. else {
  158. this.useEasing = this.options.useEasing;
  159. }
  160. };
  161. // start animation
  162. CountUp.prototype.start = function (callback) {
  163. if (this.error) {
  164. return;
  165. }
  166. this.callback = callback;
  167. if (this.duration > 0) {
  168. this.determineDirectionAndSmartEasing();
  169. this.paused = false;
  170. this.rAF = requestAnimationFrame(this.count);
  171. }
  172. else {
  173. this.printValue(this.endVal);
  174. }
  175. };
  176. // pause/resume animation
  177. CountUp.prototype.pauseResume = function () {
  178. if (!this.paused) {
  179. cancelAnimationFrame(this.rAF);
  180. }
  181. else {
  182. this.startTime = null;
  183. this.duration = this.remaining;
  184. this.startVal = this.frameVal;
  185. this.determineDirectionAndSmartEasing();
  186. this.rAF = requestAnimationFrame(this.count);
  187. }
  188. this.paused = !this.paused;
  189. };
  190. // reset to startVal so animation can be run again
  191. CountUp.prototype.reset = function () {
  192. cancelAnimationFrame(this.rAF);
  193. this.paused = true;
  194. this.resetDuration();
  195. this.startVal = this.validateValue(this.options.startVal);
  196. this.frameVal = this.startVal;
  197. this.printValue(this.startVal);
  198. };
  199. // pass a new endVal and start animation
  200. CountUp.prototype.update = function (newEndVal) {
  201. cancelAnimationFrame(this.rAF);
  202. this.startTime = null;
  203. this.endVal = this.validateValue(newEndVal);
  204. if (this.endVal === this.frameVal) {
  205. return;
  206. }
  207. this.startVal = this.frameVal;
  208. if (!this.finalEndVal) {
  209. this.resetDuration();
  210. }
  211. this.finalEndVal = null;
  212. this.determineDirectionAndSmartEasing();
  213. this.rAF = requestAnimationFrame(this.count);
  214. };
  215. CountUp.prototype.printValue = function (val) {
  216. var result = this.formattingFn(val);
  217. if (this.el.tagName === 'INPUT') {
  218. var input = this.el;
  219. input.value = result;
  220. }
  221. else if (this.el.tagName === 'text' || this.el.tagName === 'tspan') {
  222. this.el.textContent = result;
  223. }
  224. else {
  225. this.el.innerHTML = result;
  226. }
  227. };
  228. CountUp.prototype.ensureNumber = function (n) {
  229. return (typeof n === 'number' && !isNaN(n));
  230. };
  231. CountUp.prototype.validateValue = function (value) {
  232. var newValue = Number(value);
  233. if (!this.ensureNumber(newValue)) {
  234. this.error = "[CountUp] invalid start or end value: " + value;
  235. return null;
  236. }
  237. else {
  238. return newValue;
  239. }
  240. };
  241. CountUp.prototype.resetDuration = function () {
  242. this.startTime = null;
  243. this.duration = Number(this.options.duration) * 1000;
  244. this.remaining = this.duration;
  245. };
  246. return CountUp;
  247. }());
  248. export { CountUp };