123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- /*
- countUp.js
- by @inorganik
- */
- // target = id of html element or var of previously selected html element where counting occurs
- // startVal = the value you want to begin at
- // endVal = the value you want to arrive at
- // decimals = number of decimal places, default 0
- // duration = duration of animation in seconds, default 2
- // options = optional object of options (see below)
- var CountUp = function(target, startVal, endVal, decimals, duration, options) {
- var self = this;
- self.version = function () { return '1.9.3'; };
-
- // default options
- self.options = {
- useEasing: true, // toggle easing
- useGrouping: true, // 1,000,000 vs 1000000
- separator: ',', // character to use as a separator
- decimal: '.', // character to use as a decimal
- easingFn: easeOutExpo, // optional custom easing function, default is Robert Penner's easeOutExpo
- formattingFn: formatNumber, // optional custom formatting function, default is formatNumber above
- prefix: '', // optional text before the result
- suffix: '', // optional text after the result
- numerals: [] // optionally pass an array of custom numerals for 0-9
- };
- // extend default options with passed options object
- if (options && typeof options === 'object') {
- for (var key in self.options) {
- if (options.hasOwnProperty(key) && options[key] !== null) {
- self.options[key] = options[key];
- }
- }
- }
- if (self.options.separator === '') {
- self.options.useGrouping = false;
- }
- else {
- // ensure the separator is a string (formatNumber assumes this)
- self.options.separator = '' + self.options.separator;
- }
- // make sure requestAnimationFrame and cancelAnimationFrame are defined
- // polyfill for browsers without native support
- // by Opera engineer Erik Möller
- var lastTime = 0;
- var vendors = ['webkit', 'moz', 'ms', 'o'];
- for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
- window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
- window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
- }
- if (!window.requestAnimationFrame) {
- window.requestAnimationFrame = function(callback, element) {
- var currTime = new Date().getTime();
- var timeToCall = Math.max(0, 16 - (currTime - lastTime));
- var id = window.setTimeout(function() { callback(currTime + timeToCall); }, timeToCall);
- lastTime = currTime + timeToCall;
- return id;
- };
- }
- if (!window.cancelAnimationFrame) {
- window.cancelAnimationFrame = function(id) {
- clearTimeout(id);
- };
- }
- function formatNumber(num) {
- var neg = (num < 0),
- x, x1, x2, x3, i, len;
- num = Math.abs(num).toFixed(self.decimals);
- num += '';
- x = num.split('.');
- x1 = x[0];
- x2 = x.length > 1 ? self.options.decimal + x[1] : '';
- if (self.options.useGrouping) {
- x3 = '';
- for (i = 0, len = x1.length; i < len; ++i) {
- if (i !== 0 && ((i % 3) === 0)) {
- x3 = self.options.separator + x3;
- }
- x3 = x1[len - i - 1] + x3;
- }
- x1 = x3;
- }
- // optional numeral substitution
- if (self.options.numerals.length) {
- x1 = x1.replace(/[0-9]/g, function(w) {
- return self.options.numerals[+w];
- })
- x2 = x2.replace(/[0-9]/g, function(w) {
- return self.options.numerals[+w];
- })
- }
- return (neg ? '-' : '') + self.options.prefix + x1 + x2 + self.options.suffix;
- }
- // Robert Penner's easeOutExpo
- function easeOutExpo(t, b, c, d) {
- return c * (-Math.pow(2, -10 * t / d) + 1) * 1024 / 1023 + b;
- }
- function ensureNumber(n) {
- return (typeof n === 'number' && !isNaN(n));
- }
- self.initialize = function() {
- if (self.initialized) return true;
-
- self.error = '';
- self.d = (typeof target === 'string') ? document.getElementById(target) : target;
- if (!self.d) {
- self.error = '[CountUp] target is null or undefined'
- return false;
- }
- self.startVal = Number(startVal);
- self.endVal = Number(endVal);
- // error checks
- if (ensureNumber(self.startVal) && ensureNumber(self.endVal)) {
- self.decimals = Math.max(0, decimals || 0);
- self.dec = Math.pow(10, self.decimals);
- self.duration = Number(duration) * 1000 || 2000;
- self.countDown = (self.startVal > self.endVal);
- self.frameVal = self.startVal;
- self.initialized = true;
- return true;
- }
- else {
- self.error = '[CountUp] startVal ('+startVal+') or endVal ('+endVal+') is not a number';
- return false;
- }
- };
- // Print value to target
- self.printValue = function(value) {
- var result = self.options.formattingFn(value);
- if (self.d.tagName === 'INPUT') {
- this.d.value = result;
- }
- else if (self.d.tagName === 'text' || self.d.tagName === 'tspan') {
- this.d.textContent = result;
- }
- else {
- this.d.innerHTML = result;
- }
- };
- self.count = function(timestamp) {
- if (!self.startTime) { self.startTime = timestamp; }
- self.timestamp = timestamp;
- var progress = timestamp - self.startTime;
- self.remaining = self.duration - progress;
- // to ease or not to ease
- if (self.options.useEasing) {
- if (self.countDown) {
- self.frameVal = self.startVal - self.options.easingFn(progress, 0, self.startVal - self.endVal, self.duration);
- } else {
- self.frameVal = self.options.easingFn(progress, self.startVal, self.endVal - self.startVal, self.duration);
- }
- } else {
- if (self.countDown) {
- self.frameVal = self.startVal - ((self.startVal - self.endVal) * (progress / self.duration));
- } else {
- self.frameVal = self.startVal + (self.endVal - self.startVal) * (progress / self.duration);
- }
- }
- // don't go past endVal since progress can exceed duration in the last frame
- if (self.countDown) {
- self.frameVal = (self.frameVal < self.endVal) ? self.endVal : self.frameVal;
- } else {
- self.frameVal = (self.frameVal > self.endVal) ? self.endVal : self.frameVal;
- }
- // decimal
- self.frameVal = Math.round(self.frameVal*self.dec)/self.dec;
- // format and print value
- self.printValue(self.frameVal);
- // whether to continue
- if (progress < self.duration) {
- self.rAF = requestAnimationFrame(self.count);
- } else {
- if (self.callback) self.callback();
- }
- };
- // start your animation
- self.start = function(callback) {
- if (!self.initialize()) return;
- self.callback = callback;
- self.rAF = requestAnimationFrame(self.count);
- };
- // toggles pause/resume animation
- self.pauseResume = function() {
- if (!self.paused) {
- self.paused = true;
- cancelAnimationFrame(self.rAF);
- } else {
- self.paused = false;
- delete self.startTime;
- self.duration = self.remaining;
- self.startVal = self.frameVal;
- requestAnimationFrame(self.count);
- }
- };
- // reset to startVal so animation can be run again
- self.reset = function() {
- self.paused = false;
- delete self.startTime;
- self.initialized = false;
- if (self.initialize()) {
- cancelAnimationFrame(self.rAF);
- self.printValue(self.startVal);
- }
- };
- // pass a new endVal and start animation
- self.update = function (newEndVal) {
- if (!self.initialize()) return;
- newEndVal = Number(newEndVal);
- if (!ensureNumber(newEndVal)) {
- self.error = '[CountUp] update() - new endVal is not a number: '+newEndVal;
- return;
- }
- self.error = '';
- if (newEndVal === self.frameVal) return;
- cancelAnimationFrame(self.rAF);
- self.paused = false;
- delete self.startTime;
- self.startVal = self.frameVal;
- self.endVal = newEndVal;
- self.countDown = (self.startVal > self.endVal);
- self.rAF = requestAnimationFrame(self.count);
- };
- // format startVal on initialization
- if (self.initialize()) self.printValue(self.startVal);
- };
|