UNPKG

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