diff --git a/README.md b/README.md index fbb7d10..ecc1b61 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Or tinker with CountUp in [Stackblitz](https://stackblitz.com/edit/countup-types - **[Usage](#usage)** - **[Including CountUp](#including-countup)** - **[Contributing](#contributing)** +- **[Creating Animation Plugins](#creating-animation-plugins)** ## CountUp for frameworks and plugins: @@ -25,7 +26,7 @@ Or tinker with CountUp in [Stackblitz](https://stackblitz.com/edit/countup-types - **[CountUp.js with Svelte](https://gist.github.com/inorganik/85a66941ab88cc10c5fa5b26aead5f2a)** - **[CountUp.js Vue component wrapper](https://github.com/xlsdg/vue-countup-v2)** - **[CountUp.js WordPress Plugin](https://wordpress.org/plugins/countup-js/)** -- **[CountUp.js jQuery Plugin](https://gist.github.com/inorganik/b63dbe5b3810ff2c0175aee4670a4732)** +- **[CountUp.js with jQuery](https://gist.github.com/inorganik/b63dbe5b3810ff2c0175aee4670a4732)** ## Features @@ -67,6 +68,7 @@ interface CountUpOptions { scrollSpyDelay?: number; // delay (ms) after target comes into view scrollSpyOnce?: boolean; // run only once onCompleteCallback?: () => any; // gets called when animation completes + plugin?: CountUpPlugin; // for alternate animations } ``` @@ -207,3 +209,30 @@ Before you make a pull request, please be sure to follow these instructions: 4. npm publish --> + +--- + +## Creating Animation Plugins + +CountUp supports plugins as of v2.6.0. Plugins implement their own render method to display each frame's formatted value. A class instance or object can be passed to the `plugin` property of CountUpOptions, and the plugin's render method will be called instead of CountUp's. + +```ts +export declare interface CountUpPlugin { + render(elem: HTMLElement, formatted: string): void; +} +``` + +An example of a plugin: +```ts +export class SomePlugin implements CountUpPlugin { + // ...some properties here + + constructor(options: SomePluginOptions) { + // ...setup code here if you need it + } + + render(elem: HTMLElement, formatted: string): void { + // render DOM here + } +} +``` diff --git a/dist/countUp.d.ts b/dist/countUp.d.ts index 3f50a6f..628dc18 100644 --- a/dist/countUp.d.ts +++ b/dist/countUp.d.ts @@ -18,19 +18,23 @@ export interface CountUpOptions { scrollSpyDelay?: number; scrollSpyOnce?: boolean; onCompleteCallback?: () => any; + plugin?: CountUpPlugin; +} +export declare interface CountUpPlugin { + render(elem: HTMLElement, formatted: string): void; } export declare class CountUp { private endVal; options?: CountUpOptions; version: string; private defaults; - private el; private rAF; private startTime; private remaining; private finalEndVal; private useEasing; private countDown; + el: HTMLElement | HTMLInputElement; formattingFn: (num: number) => string; easingFn?: (t: number, b: number, c: number, d: number) => number; error: string; diff --git a/dist/countUp.js b/dist/countUp.js index 08c2b9d..539a7d5 100644 --- a/dist/countUp.js +++ b/dist/countUp.js @@ -15,7 +15,7 @@ var CountUp = /** @class */ (function () { var _this = this; this.endVal = endVal; this.options = options; - this.version = '2.5.0'; + this.version = '2.6.0'; this.defaults = { startVal: 0, decimalPlaces: 0, @@ -258,9 +258,14 @@ var CountUp = /** @class */ (function () { this.rAF = requestAnimationFrame(this.count); }; CountUp.prototype.printValue = function (val) { - var result = this.formattingFn(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; diff --git a/dist/countUp.min.js b/dist/countUp.min.js index 88c613e..5a013b4 100644 --- a/dist/countUp.min.js +++ b/dist/countUp.min.js @@ -1 +1 @@ -var __assign=this&&this.__assign||function(){return(__assign=Object.assign||function(t){for(var i,n=1,s=arguments.length;ns.endVal;s.frameVal=n?s.endVal:s.frameVal,s.frameVal=Number(s.frameVal.toFixed(s.options.decimalPlaces)),s.printValue(s.frameVal),i1?s.options.decimal+r[1]:"",s.options.useGrouping){e="";for(var l=3,h=0,u=0,p=n.length;uwindow.scrollY&&t.paused?(t.paused=!1,setTimeout(function(){return t.start()},t.options.scrollSpyDelay),t.options.scrollSpyOnce&&(t.once=!0)):(window.scrollY>a||s>i)&&!t.paused&&t.reset()}},t.prototype.determineDirectionAndSmartEasing=function(){var t=this.finalEndVal?this.finalEndVal:this.endVal;this.countDown=this.startVal>t;var i=t-this.startVal;if(Math.abs(i)>this.options.smartEasingThreshold&&this.options.useEasing){this.finalEndVal=t;var n=this.countDown?1:-1;this.endVal=t+n*this.options.smartEasingAmount,this.duration=this.duration/2}else this.endVal=t,this.finalEndVal=null;null!==this.finalEndVal?this.useEasing=!1:this.useEasing=this.options.useEasing},t.prototype.start=function(t){this.error||(t&&(this.options.onCompleteCallback=t),this.duration>0?(this.determineDirectionAndSmartEasing(),this.paused=!1,this.rAF=requestAnimationFrame(this.count)):this.printValue(this.endVal))},t.prototype.pauseResume=function(){this.paused?(this.startTime=null,this.duration=this.remaining,this.startVal=this.frameVal,this.determineDirectionAndSmartEasing(),this.rAF=requestAnimationFrame(this.count)):cancelAnimationFrame(this.rAF),this.paused=!this.paused},t.prototype.reset=function(){cancelAnimationFrame(this.rAF),this.paused=!0,this.resetDuration(),this.startVal=this.validateValue(this.options.startVal),this.frameVal=this.startVal,this.printValue(this.startVal)},t.prototype.update=function(t){cancelAnimationFrame(this.rAF),this.startTime=null,this.endVal=this.validateValue(t),this.endVal!==this.frameVal&&(this.startVal=this.frameVal,null==this.finalEndVal&&this.resetDuration(),this.finalEndVal=null,this.determineDirectionAndSmartEasing(),this.rAF=requestAnimationFrame(this.count))},t.prototype.printValue=function(t){var i=this.formattingFn(t);this.el&&("INPUT"===this.el.tagName?this.el.value=i:"text"===this.el.tagName||"tspan"===this.el.tagName?this.el.textContent=i:this.el.innerHTML=i)},t.prototype.ensureNumber=function(t){return"number"==typeof t&&!isNaN(t)},t.prototype.validateValue=function(t){var i=Number(t);return this.ensureNumber(i)?i:(this.error="[CountUp] invalid start or end value: ".concat(t),null)},t.prototype.resetDuration=function(){this.startTime=null,this.duration=1e3*Number(this.options.duration),this.remaining=this.duration},t}();export{CountUp}; \ No newline at end of file +var __assign=this&&this.__assign||function(){return(__assign=Object.assign||function(t){for(var i,n=1,s=arguments.length;ns.endVal;s.frameVal=n?s.endVal:s.frameVal,s.frameVal=Number(s.frameVal.toFixed(s.options.decimalPlaces)),s.printValue(s.frameVal),i1?s.options.decimal+r[1]:"",s.options.useGrouping){e="";for(var l=3,h=0,u=0,p=n.length;uwindow.scrollY&&t.paused?(t.paused=!1,setTimeout(function(){return t.start()},t.options.scrollSpyDelay),t.options.scrollSpyOnce&&(t.once=!0)):(window.scrollY>a||s>i)&&!t.paused&&t.reset()}},t.prototype.determineDirectionAndSmartEasing=function(){var t=this.finalEndVal?this.finalEndVal:this.endVal;this.countDown=this.startVal>t;var i=t-this.startVal;if(Math.abs(i)>this.options.smartEasingThreshold&&this.options.useEasing){this.finalEndVal=t;var n=this.countDown?1:-1;this.endVal=t+n*this.options.smartEasingAmount,this.duration=this.duration/2}else this.endVal=t,this.finalEndVal=null;null!==this.finalEndVal?this.useEasing=!1:this.useEasing=this.options.useEasing},t.prototype.start=function(t){this.error||(t&&(this.options.onCompleteCallback=t),this.duration>0?(this.determineDirectionAndSmartEasing(),this.paused=!1,this.rAF=requestAnimationFrame(this.count)):this.printValue(this.endVal))},t.prototype.pauseResume=function(){this.paused?(this.startTime=null,this.duration=this.remaining,this.startVal=this.frameVal,this.determineDirectionAndSmartEasing(),this.rAF=requestAnimationFrame(this.count)):cancelAnimationFrame(this.rAF),this.paused=!this.paused},t.prototype.reset=function(){cancelAnimationFrame(this.rAF),this.paused=!0,this.resetDuration(),this.startVal=this.validateValue(this.options.startVal),this.frameVal=this.startVal,this.printValue(this.startVal)},t.prototype.update=function(t){cancelAnimationFrame(this.rAF),this.startTime=null,this.endVal=this.validateValue(t),this.endVal!==this.frameVal&&(this.startVal=this.frameVal,null==this.finalEndVal&&this.resetDuration(),this.finalEndVal=null,this.determineDirectionAndSmartEasing(),this.rAF=requestAnimationFrame(this.count))},t.prototype.printValue=function(t){var i;if(this.el){var n=this.formattingFn(t);if(null!==(i=this.options.plugin)&&void 0!==i&&i.render)this.options.plugin.render(this.el,n);else if("INPUT"===this.el.tagName)this.el.value=n;else"text"===this.el.tagName||"tspan"===this.el.tagName?this.el.textContent=n:this.el.innerHTML=n}},t.prototype.ensureNumber=function(t){return"number"==typeof t&&!isNaN(t)},t.prototype.validateValue=function(t){var i=Number(t);return this.ensureNumber(i)?i:(this.error="[CountUp] invalid start or end value: ".concat(t),null)},t.prototype.resetDuration=function(){this.startTime=null,this.duration=1e3*Number(this.options.duration),this.remaining=this.duration},t}();export{CountUp}; \ No newline at end of file diff --git a/dist/countUp.umd.js b/dist/countUp.umd.js index d69c7b6..9fdde5a 100644 --- a/dist/countUp.umd.js +++ b/dist/countUp.umd.js @@ -21,7 +21,7 @@ var _this = this; this.endVal = endVal; this.options = options; - this.version = '2.5.0'; + this.version = '2.6.0'; this.defaults = { startVal: 0, decimalPlaces: 0, @@ -264,9 +264,14 @@ this.rAF = requestAnimationFrame(this.count); }; CountUp.prototype.printValue = function (val) { - var result = this.formattingFn(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; diff --git a/dist/countUp.withPolyfill.min.js b/dist/countUp.withPolyfill.min.js index c5e2a21..d9a7d72 100644 --- a/dist/countUp.withPolyfill.min.js +++ b/dist/countUp.withPolyfill.min.js @@ -1 +1 @@ -!function(){for(var t=0,i=["webkit","moz","ms","o"],n=0;na.endVal;a.frameVal=n?a.endVal:a.frameVal,a.frameVal=Number(a.frameVal.toFixed(a.options.decimalPlaces)),a.printValue(a.frameVal),i1?a.options.decimal+r[1]:"",a.options.useGrouping){e="";for(var l=3,u=0,h=0,p=n.length;hwindow.scrollY&&t.paused?(t.paused=!1,setTimeout(function(){return t.start()},t.options.scrollSpyDelay),t.options.scrollSpyOnce&&(t.once=!0)):(window.scrollY>s||a>i)&&!t.paused&&t.reset()}},t.prototype.determineDirectionAndSmartEasing=function(){var t=this.finalEndVal?this.finalEndVal:this.endVal;this.countDown=this.startVal>t;var i=t-this.startVal;if(Math.abs(i)>this.options.smartEasingThreshold&&this.options.useEasing){this.finalEndVal=t;var n=this.countDown?1:-1;this.endVal=t+n*this.options.smartEasingAmount,this.duration=this.duration/2}else this.endVal=t,this.finalEndVal=null;null!==this.finalEndVal?this.useEasing=!1:this.useEasing=this.options.useEasing},t.prototype.start=function(t){this.error||(t&&(this.options.onCompleteCallback=t),this.duration>0?(this.determineDirectionAndSmartEasing(),this.paused=!1,this.rAF=requestAnimationFrame(this.count)):this.printValue(this.endVal))},t.prototype.pauseResume=function(){this.paused?(this.startTime=null,this.duration=this.remaining,this.startVal=this.frameVal,this.determineDirectionAndSmartEasing(),this.rAF=requestAnimationFrame(this.count)):cancelAnimationFrame(this.rAF),this.paused=!this.paused},t.prototype.reset=function(){cancelAnimationFrame(this.rAF),this.paused=!0,this.resetDuration(),this.startVal=this.validateValue(this.options.startVal),this.frameVal=this.startVal,this.printValue(this.startVal)},t.prototype.update=function(t){cancelAnimationFrame(this.rAF),this.startTime=null,this.endVal=this.validateValue(t),this.endVal!==this.frameVal&&(this.startVal=this.frameVal,null==this.finalEndVal&&this.resetDuration(),this.finalEndVal=null,this.determineDirectionAndSmartEasing(),this.rAF=requestAnimationFrame(this.count))},t.prototype.printValue=function(t){var i=this.formattingFn(t);this.el&&("INPUT"===this.el.tagName?this.el.value=i:"text"===this.el.tagName||"tspan"===this.el.tagName?this.el.textContent=i:this.el.innerHTML=i)},t.prototype.ensureNumber=function(t){return"number"==typeof t&&!isNaN(t)},t.prototype.validateValue=function(t){var i=Number(t);return this.ensureNumber(i)?i:(this.error="[CountUp] invalid start or end value: ".concat(t),null)},t.prototype.resetDuration=function(){this.startTime=null,this.duration=1e3*Number(this.options.duration),this.remaining=this.duration},t}();export{CountUp}; \ No newline at end of file +!function(){for(var t=0,i=["webkit","moz","ms","o"],n=0;na.endVal;a.frameVal=n?a.endVal:a.frameVal,a.frameVal=Number(a.frameVal.toFixed(a.options.decimalPlaces)),a.printValue(a.frameVal),i1?a.options.decimal+r[1]:"",a.options.useGrouping){e="";for(var l=3,u=0,h=0,p=n.length;hwindow.scrollY&&t.paused?(t.paused=!1,setTimeout(function(){return t.start()},t.options.scrollSpyDelay),t.options.scrollSpyOnce&&(t.once=!0)):(window.scrollY>s||a>i)&&!t.paused&&t.reset()}},t.prototype.determineDirectionAndSmartEasing=function(){var t=this.finalEndVal?this.finalEndVal:this.endVal;this.countDown=this.startVal>t;var i=t-this.startVal;if(Math.abs(i)>this.options.smartEasingThreshold&&this.options.useEasing){this.finalEndVal=t;var n=this.countDown?1:-1;this.endVal=t+n*this.options.smartEasingAmount,this.duration=this.duration/2}else this.endVal=t,this.finalEndVal=null;null!==this.finalEndVal?this.useEasing=!1:this.useEasing=this.options.useEasing},t.prototype.start=function(t){this.error||(t&&(this.options.onCompleteCallback=t),this.duration>0?(this.determineDirectionAndSmartEasing(),this.paused=!1,this.rAF=requestAnimationFrame(this.count)):this.printValue(this.endVal))},t.prototype.pauseResume=function(){this.paused?(this.startTime=null,this.duration=this.remaining,this.startVal=this.frameVal,this.determineDirectionAndSmartEasing(),this.rAF=requestAnimationFrame(this.count)):cancelAnimationFrame(this.rAF),this.paused=!this.paused},t.prototype.reset=function(){cancelAnimationFrame(this.rAF),this.paused=!0,this.resetDuration(),this.startVal=this.validateValue(this.options.startVal),this.frameVal=this.startVal,this.printValue(this.startVal)},t.prototype.update=function(t){cancelAnimationFrame(this.rAF),this.startTime=null,this.endVal=this.validateValue(t),this.endVal!==this.frameVal&&(this.startVal=this.frameVal,null==this.finalEndVal&&this.resetDuration(),this.finalEndVal=null,this.determineDirectionAndSmartEasing(),this.rAF=requestAnimationFrame(this.count))},t.prototype.printValue=function(t){var i;if(this.el){var n=this.formattingFn(t);if(null!==(i=this.options.plugin)&&void 0!==i&&i.render)this.options.plugin.render(this.el,n);else if("INPUT"===this.el.tagName)this.el.value=n;else"text"===this.el.tagName||"tspan"===this.el.tagName?this.el.textContent=n:this.el.innerHTML=n}},t.prototype.ensureNumber=function(t){return"number"==typeof t&&!isNaN(t)},t.prototype.validateValue=function(t){var i=Number(t);return this.ensureNumber(i)?i:(this.error="[CountUp] invalid start or end value: ".concat(t),null)},t.prototype.resetDuration=function(){this.startTime=null,this.duration=1e3*Number(this.options.duration),this.remaining=this.duration},t}();export{CountUp}; \ No newline at end of file diff --git a/package.json b/package.json index f073cf4..91136e4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "countup.js", "description": "Animates a numerical value by counting to it", - "version": "2.5.0", + "version": "2.6.0", "license": "MIT", "main": "./dist/countUp.umd.js", "module": "./dist/countUp.min.js", diff --git a/src/countUp.spec.ts b/src/countUp.spec.ts index cc1c026..01bb99d 100644 --- a/src/countUp.spec.ts +++ b/src/countUp.spec.ts @@ -1,4 +1,4 @@ -import { CountUp } from './countUp'; +import { CountUp, CountUpPlugin } from './countUp'; describe('CountUp', () => { @@ -262,5 +262,20 @@ describe('CountUp', () => { expect(getTargetHtml()).toEqual('100'); expect(callbackSpy).toHaveBeenCalled(); }); + + it('should respect the plugin option', () => { + const plugin: CountUpPlugin = { + render: (el, result) => { + el.innerHTML = result; + } + }; + countUp = new CountUp('target', 1000, { + plugin, + useGrouping: true + }); + countUp.start(); + + expect(getTargetHtml()).toEqual('1,000'); + }); }); }); diff --git a/src/countUp.ts b/src/countUp.ts index 5ece9f5..45372f3 100644 --- a/src/countUp.ts +++ b/src/countUp.ts @@ -19,12 +19,17 @@ export interface CountUpOptions { // (default) scrollSpyDelay?: number; // delay (ms) after target comes into view scrollSpyOnce?: boolean; // run only once onCompleteCallback?: () => any; // gets called when animation completes + plugin?: CountUpPlugin; // for alternate animations +} + +export declare interface CountUpPlugin { + render(elem: HTMLElement, formatted: string): void; } // playground: stackblitz.com/edit/countup-typescript export class CountUp { - version = '2.5.0'; + version = '2.6.0'; private defaults: CountUpOptions = { startVal: 0, decimalPlaces: 0, @@ -42,13 +47,13 @@ export class CountUp { scrollSpyDelay: 200, scrollSpyOnce: false, }; - private el: HTMLElement | HTMLInputElement; private rAF: any; private startTime: number; private remaining: number; private finalEndVal: number = null; // for smart easing private useEasing = true; private countDown = false; + el: HTMLElement | HTMLInputElement; formattingFn: (num: number) => string; easingFn?: (t: number, b: number, c: number, d: number) => number; error = ''; @@ -252,8 +257,12 @@ export class CountUp { } printValue(val: number): void { - const result = this.formattingFn(val); if (!this.el) return; + const result = this.formattingFn(val); + if (this.options.plugin?.render) { + this.options.plugin.render(this.el, result); + return; + } if (this.el.tagName === 'INPUT') { const input = this.el as HTMLInputElement; input.value = result;