Files

365 lines
14 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
/**
* Animates a number by counting to it.
* playground: stackblitz.com/edit/countup-typescript
*
* @param target - id of html element, input, svg text element, or DOM element reference where counting occurs.
* @param endVal - the value you want to arrive at.
* @param options - optional configuration object for fine-grain control
*/
var CountUp = /** @class */ (function () {
function CountUp(target, endVal, options) {
var _this = this;
this.endVal = endVal;
this.options = options;
this.version = '2.10.0';
this.defaults = {
startVal: 0,
decimalPlaces: 0,
duration: 2,
useEasing: true,
useGrouping: true,
useIndianSeparators: false,
smartEasingThreshold: 999,
smartEasingAmount: 333,
separator: ',',
decimal: '.',
prefix: '',
suffix: '',
autoAnimate: false,
autoAnimateDelay: 200,
autoAnimateOnce: false,
};
this.finalEndVal = null; // for smart easing
this.useEasing = true;
this.countDown = false;
this.error = '';
this.startVal = 0;
this.paused = true;
this.once = false;
/** Animation frame callback — advances the value each frame. */
this.count = function (timestamp) {
if (!_this.startTime) {
_this.startTime = timestamp;
}
var progress = timestamp - _this.startTime;
_this.remaining = _this.duration - progress;
// to ease or not to ease
if (_this.useEasing) {
if (_this.countDown) {
_this.frameVal = _this.startVal - _this.easingFn(progress, 0, _this.startVal - _this.endVal, _this.duration);
}
else {
_this.frameVal = _this.easingFn(progress, _this.startVal, _this.endVal - _this.startVal, _this.duration);
}
}
else {
_this.frameVal = _this.startVal + (_this.endVal - _this.startVal) * (progress / _this.duration);
}
// don't go past endVal since progress can exceed duration in the last frame
var wentPast = _this.countDown ? _this.frameVal < _this.endVal : _this.frameVal > _this.endVal;
_this.frameVal = wentPast ? _this.endVal : _this.frameVal;
// decimal
_this.frameVal = Number(_this.frameVal.toFixed(_this.options.decimalPlaces));
// format and print value
_this.printValue(_this.frameVal);
// whether to continue
if (progress < _this.duration) {
_this.rAF = requestAnimationFrame(_this.count);
}
else if (_this.finalEndVal !== null) {
// smart easing
_this.update(_this.finalEndVal);
}
else {
if (_this.options.onCompleteCallback) {
_this.options.onCompleteCallback();
}
}
};
/** Default number formatter with grouping, decimals, prefix/suffix, and numeral substitution. */
this.formatNumber = function (num) {
var neg = (num < 0) ? '-' : '';
var result, x1, x2, x3;
result = Math.abs(num).toFixed(_this.options.decimalPlaces);
result += '';
var x = result.split('.');
x1 = x[0];
x2 = x.length > 1 ? _this.options.decimal + x[1] : '';
if (_this.options.useGrouping) {
x3 = '';
var factor = 3, j = 0;
for (var i = 0, len = x1.length; i < len; ++i) {
if (_this.options.useIndianSeparators && i === 4) {
factor = 2;
j = 1;
}
if (i !== 0 && (j % factor) === 0) {
x3 = _this.options.separator + x3;
}
j++;
x3 = x1[len - i - 1] + x3;
}
x1 = x3;
}
// optional numeral substitution
if (_this.options.numerals && _this.options.numerals.length) {
x1 = x1.replace(/[0-9]/g, function (w) { return _this.options.numerals[+w]; });
x2 = x2.replace(/[0-9]/g, function (w) { return _this.options.numerals[+w]; });
}
return neg + _this.options.prefix + x1 + x2 + _this.options.suffix;
};
/**
* Default easing function (easeOutExpo).
* @param t current time
* @param b beginning value
* @param c change in value
* @param d duration
*/
this.easeOutExpo = function (t, b, c, d) {
return c * (-Math.pow(2, -10 * t / d) + 1) * 1024 / 1023 + b;
};
this.options = __assign(__assign({}, this.defaults), options);
if (this.options.enableScrollSpy) {
this.options.autoAnimate = true;
}
if (this.options.scrollSpyDelay !== undefined) {
this.options.autoAnimateDelay = this.options.scrollSpyDelay;
}
if (this.options.scrollSpyOnce) {
this.options.autoAnimateOnce = true;
}
this.formattingFn = (this.options.formattingFn) ?
this.options.formattingFn : this.formatNumber;
this.easingFn = (this.options.easingFn) ?
this.options.easingFn : this.easeOutExpo;
this.el = (typeof target === 'string') ? document.getElementById(target) : target;
endVal = endVal == null ? this.parse(this.el.innerHTML) : endVal;
this.startVal = this.validateValue(this.options.startVal);
this.frameVal = this.startVal;
this.endVal = this.validateValue(endVal);
this.options.decimalPlaces = Math.max(0 || this.options.decimalPlaces);
this.resetDuration();
this.options.separator = String(this.options.separator);
this.useEasing = this.options.useEasing;
if (this.options.separator === '') {
this.options.useGrouping = false;
}
if (this.el) {
this.printValue(this.startVal);
}
else {
this.error = '[CountUp] target is null or undefined';
}
if (typeof window !== 'undefined' && this.options.autoAnimate) {
if (!this.error && typeof IntersectionObserver !== 'undefined') {
this.setupObserver();
}
else {
if (this.error) {
console.error(this.error, target);
}
else {
console.error('IntersectionObserver is not supported by this browser');
}
}
}
}
/** Set up an IntersectionObserver to auto-animate when the target element appears. */
CountUp.prototype.setupObserver = function () {
var _this = this;
var existing = CountUp.observedElements.get(this.el);
if (existing) {
existing.unobserve();
}
CountUp.observedElements.set(this.el, this);
this.observer = new IntersectionObserver(function (entries) {
for (var _i = 0, entries_1 = entries; _i < entries_1.length; _i++) {
var entry = entries_1[_i];
if (entry.isIntersecting && _this.paused && !_this.once) {
_this.paused = false;
_this.autoAnimateTimeout = setTimeout(function () { return _this.start(); }, _this.options.autoAnimateDelay);
if (_this.options.autoAnimateOnce) {
_this.once = true;
_this.observer.disconnect();
}
}
else if (!entry.isIntersecting && !_this.paused) {
clearTimeout(_this.autoAnimateTimeout);
_this.reset();
}
}
}, { threshold: 0 });
this.observer.observe(this.el);
};
/** Disconnect the IntersectionObserver and stop watching this element. */
CountUp.prototype.unobserve = function () {
var _a;
clearTimeout(this.autoAnimateTimeout);
(_a = this.observer) === null || _a === void 0 ? void 0 : _a.disconnect();
CountUp.observedElements.delete(this.el);
};
/** Teardown: cancel animation, disconnect observer, clear callbacks. */
CountUp.prototype.onDestroy = function () {
clearTimeout(this.autoAnimateTimeout);
cancelAnimationFrame(this.rAF);
this.paused = true;
this.unobserve();
this.options.onCompleteCallback = null;
this.options.onStartCallback = null;
};
/**
* Smart easing works by breaking the animation into 2 parts, the second part being the
* smartEasingAmount and first part being the total amount minus the smartEasingAmount. It works
* by disabling easing for the first part and enabling it on the second part. It is used if
* useEasing is true and the total animation amount exceeds the smartEasingThreshold.
*/
CountUp.prototype.determineDirectionAndSmartEasing = function () {
var end = (this.finalEndVal) ? this.finalEndVal : this.endVal;
this.countDown = (this.startVal > end);
var animateAmount = end - this.startVal;
if (Math.abs(animateAmount) > this.options.smartEasingThreshold && this.options.useEasing) {
this.finalEndVal = end;
var up = (this.countDown) ? 1 : -1;
this.endVal = end + (up * this.options.smartEasingAmount);
this.duration = this.duration / 2;
}
else {
this.endVal = end;
this.finalEndVal = null;
}
if (this.finalEndVal !== null) {
// setting finalEndVal indicates smart easing
this.useEasing = false;
}
else {
this.useEasing = this.options.useEasing;
}
};
/** Start the animation. Optionally pass a callback that fires on completion. */
CountUp.prototype.start = function (callback) {
if (this.error) {
return;
}
if (this.options.onStartCallback) {
this.options.onStartCallback();
}
if (callback) {
this.options.onCompleteCallback = callback;
}
if (this.duration > 0) {
this.determineDirectionAndSmartEasing();
this.paused = false;
this.rAF = requestAnimationFrame(this.count);
}
else {
this.printValue(this.endVal);
}
};
/** Toggle pause/resume on the animation. */
CountUp.prototype.pauseResume = function () {
if (!this.paused) {
cancelAnimationFrame(this.rAF);
}
else {
this.startTime = null;
this.duration = this.remaining;
this.startVal = this.frameVal;
this.determineDirectionAndSmartEasing();
this.rAF = requestAnimationFrame(this.count);
}
this.paused = !this.paused;
};
/** Reset to startVal so the animation can be run again. */
CountUp.prototype.reset = function () {
clearTimeout(this.autoAnimateTimeout);
cancelAnimationFrame(this.rAF);
this.paused = true;
this.once = false;
this.resetDuration();
this.startVal = this.validateValue(this.options.startVal);
this.frameVal = this.startVal;
this.printValue(this.startVal);
};
/** Pass a new endVal and start the animation. */
CountUp.prototype.update = function (newEndVal) {
cancelAnimationFrame(this.rAF);
this.startTime = null;
this.endVal = this.validateValue(newEndVal);
if (this.endVal === this.frameVal) {
return;
}
this.startVal = this.frameVal;
if (this.finalEndVal == null) {
this.resetDuration();
}
this.finalEndVal = null;
this.determineDirectionAndSmartEasing();
this.rAF = requestAnimationFrame(this.count);
};
/** Format and render the given value to the target element. */
CountUp.prototype.printValue = function (val) {
var _a;
if (!this.el)
return;
var result = this.formattingFn(val);
if ((_a = this.options.plugin) === null || _a === void 0 ? void 0 : _a.render) {
this.options.plugin.render(this.el, result);
return;
}
if (this.el.tagName === 'INPUT') {
var input = this.el;
input.value = result;
}
else if (this.el.tagName === 'text' || this.el.tagName === 'tspan') {
this.el.textContent = result;
}
else {
this.el.innerHTML = result;
}
};
/** Return true if the value is a finite number. */
CountUp.prototype.ensureNumber = function (n) {
return (typeof n === 'number' && !isNaN(n));
};
/** Validate and convert a value to a number, setting an error if invalid. */
CountUp.prototype.validateValue = function (value) {
var newValue = Number(value);
if (!this.ensureNumber(newValue)) {
this.error = "[CountUp] invalid start or end value: ".concat(value);
return null;
}
else {
return newValue;
}
};
/** Reset startTime, duration, and remaining to their initial values. */
CountUp.prototype.resetDuration = function () {
this.startTime = null;
this.duration = Number(this.options.duration) * 1000;
this.remaining = this.duration;
};
/** Parse a formatted string back to a number using the current separator/decimal options. */
CountUp.prototype.parse = function (number) {
// eslint-disable-next-line no-irregular-whitespace
var escapeRegExp = function (s) { return s.replace(/([.,'  ])/g, '\\$1'); };
var sep = escapeRegExp(this.options.separator);
var dec = escapeRegExp(this.options.decimal);
var num = number.replace(new RegExp(sep, 'g'), '').replace(new RegExp(dec, 'g'), '.');
return parseFloat(num);
};
CountUp.observedElements = new WeakMap();
return CountUp;
}());
export { CountUp };