qarl 1.2.5 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ var R=Object.create;var L=Object.defineProperty;var j=Object.getOwnPropertyDescriptor;var V=Object.getOwnPropertyNames;var B=Object.getPrototypeOf,G=Object.prototype.hasOwnProperty;var q=(t,s,e)=>s in t?L(t,s,{enumerable:!0,configurable:!0,writable:!0,value:e}):t[s]=e;var N=(t,s)=>()=>(s||t((s={exports:{}}).exports,s),s.exports);var Y=(t,s,e,i)=>{if(s&&typeof s=="object"||typeof s=="function")for(let r of V(s))!G.call(t,r)&&r!==e&&L(t,r,{get:()=>s[r],enumerable:!(i=j(s,r))||i.enumerable});return t};var z=(t,s,e)=>(e=t!=null?R(B(t)):{},Y(s||!t||!t.__esModule?L(e,"default",{value:t,enumerable:!0}):e,t));var y=(t,s,e)=>(q(t,typeof s!="symbol"?s+"":s,e),e);var k=N(($,b)=>{"use strict";var K=Object.prototype.hasOwnProperty,g="~";function T(){}Object.create&&(T.prototype=Object.create(null),new T().__proto__||(g=!1));function H(t,s,e){this.fn=t,this.context=s,this.once=e||!1}function F(t,s,e,i,r){if(typeof e!="function")throw new TypeError("The listener must be a function");var n=new H(e,i||t,r),a=g?g+s:s;return t._events[a]?t._events[a].fn?t._events[a]=[t._events[a],n]:t._events[a].push(n):(t._events[a]=n,t._eventsCount++),t}function D(t,s){--t._eventsCount===0?t._events=new T:delete t._events[s]}function f(){this._events=new T,this._eventsCount=0}f.prototype.eventNames=function(){var s=[],e,i;if(this._eventsCount===0)return s;for(i in e=this._events)K.call(e,i)&&s.push(g?i.slice(1):i);return Object.getOwnPropertySymbols?s.concat(Object.getOwnPropertySymbols(e)):s};f.prototype.listeners=function(s){var e=g?g+s:s,i=this._events[e];if(!i)return[];if(i.fn)return[i.fn];for(var r=0,n=i.length,a=new Array(n);r<n;r++)a[r]=i[r].fn;return a};f.prototype.listenerCount=function(s){var e=g?g+s:s,i=this._events[e];return i?i.fn?1:i.length:0};f.prototype.emit=function(s,e,i,r,n,a){var p=g?g+s:s;if(!this._events[p])return!1;var o=this._events[p],l=arguments.length,m,h;if(o.fn){switch(o.once&&this.removeListener(s,o.fn,void 0,!0),l){case 1:return o.fn.call(o.context),!0;case 2:return o.fn.call(o.context,e),!0;case 3:return o.fn.call(o.context,e,i),!0;case 4:return o.fn.call(o.context,e,i,r),!0;case 5:return o.fn.call(o.context,e,i,r,n),!0;case 6:return o.fn.call(o.context,e,i,r,n,a),!0}for(h=1,m=new Array(l-1);h<l;h++)m[h-1]=arguments[h];o.fn.apply(o.context,m)}else{var I=o.length,c;for(h=0;h<I;h++)switch(o[h].once&&this.removeListener(s,o[h].fn,void 0,!0),l){case 1:o[h].fn.call(o[h].context);break;case 2:o[h].fn.call(o[h].context,e);break;case 3:o[h].fn.call(o[h].context,e,i);break;case 4:o[h].fn.call(o[h].context,e,i,r);break;default:if(!m)for(c=1,m=new Array(l-1);c<l;c++)m[c-1]=arguments[c];o[h].fn.apply(o[h].context,m)}}return!0};f.prototype.on=function(s,e,i){return F(this,s,e,i,!1)};f.prototype.once=function(s,e,i){return F(this,s,e,i,!0)};f.prototype.removeListener=function(s,e,i,r){var n=g?g+s:s;if(!this._events[n])return this;if(!e)return D(this,n),this;var a=this._events[n];if(a.fn)a.fn===e&&(!r||a.once)&&(!i||a.context===i)&&D(this,n);else{for(var p=0,o=[],l=a.length;p<l;p++)(a[p].fn!==e||r&&!a[p].once||i&&a[p].context!==i)&&o.push(a[p]);o.length?this._events[n]=o.length===1?o[0]:o:D(this,n)}return this};f.prototype.removeAllListeners=function(s){var e;return s?(e=g?g+s:s,this._events[e]&&D(this,e)):(this._events=new T,this._eventsCount=0),this};f.prototype.off=f.prototype.removeListener;f.prototype.addListener=f.prototype.on;f.prefixed=g;f.EventEmitter=f;typeof b<"u"&&(b.exports=f)});var U=z(k(),1);var Q=U.default;var C={processors:[],time:0,loop:!1,mode:null,delay:0,repeat:0,target:null,easing:t=>t,reversed:!1,repeatDelay:0,autoApplyProcessors:!0};var _={PLAY:"play",STOP:"stop",BEGIN:"begin",UPDATE:"update",REPEAT:"repeat",COMPLETE:"complete"};var x=Math.PI*2,v=Math.pow,E={reverse:t=>1-t,linear:t=>t,yoyo:t=>1-Math.abs(1-t*2),inQuad:t=>v(t,2),outQuad:t=>1-(1-t)*(1-t),inOutQuad:t=>t<.5?2*t*t:-1+(4-2*t)*t,inCubic:t=>v(t,3),outCubic:t=>--t*t*t+1,inOutCubic:t=>t<.5?4*v(t,3):(t-1)*(2*t-2)*(2*t-2)+1,inQuart:t=>v(t,4),outQuart:t=>1- --t*v(t,3),inOutQuart:t=>t<.5?8*v(t,4):1-8*--t*t*t*t,inQuint:t=>v(t,5),outQuint:t=>1+--t*v(t,4),inOutQuint:t=>t<.5?16*v(t,5):1+16*--t*t*t*t*t,inSine:t=>1-Math.cos(t*Math.PI/2),outSine:t=>Math.sin(t*Math.PI/2),inOutSine:t=>-(Math.cos(Math.PI*t)-1)/2,inBack:(t,s=1.70158)=>t*t*((s+1)*t-s),outBack:(t,s=1.70158)=>(t-=1)*t*((s+1)*t+s)+1,inOutBack:(t,s=1.70158)=>(t*=2)<1?.5*(t*t*((s*1.525+1)*t-s*1.525)):.5*((t-=2)*t*((s*1.525+1)*t+s*1.525)+2),inExpo:t=>t===0?0:Math.pow(2,10*(t-1)),outExpo:t=>t===1?1:1-Math.pow(2,-10*t),inOutExpo:t=>t===0?0:t===1?1:t<.5?.5*Math.pow(2,20*t-10):1-.5*Math.pow(2,-20*t+10),inElastic:(t,s=1,e=.5)=>t===0?0:t===1?1:-s*Math.pow(2,10*(t-1))*Math.sin((t-1-e/x*Math.asin(1/s))*x/e),outElastic:(t,s=1,e=.5)=>t===0?0:t===1?1:s*Math.pow(2,-10*t)*Math.sin((t-e/x*Math.asin(1/s))*x/e)+1,inOutElastic:(t,s=1,e=.5)=>t===0?0:t===1?1:t<.5?-.5*s*Math.pow(2,20*t-10)*Math.sin((20*t-11.125)*x/e):.5*s*Math.pow(2,-20*t+10)*Math.sin((20*t-11.125)*x/e)+1};var O={pingPong:function(t){return t<=.5?this._easing(t*2):1-this._easing((t-.5)*2)},yoyo:function(t){return this._easing(E.yoyo(t))},bounce:function(t){return E.yoyo(this._easing(t))}};var J=0,d=class d extends Q{constructor(e={},i){super();y(this,"_reversedEasing",e=>this.settings.easing(1-e));y(this,"_stepDelay",e=>{this.remainingDelay-=e,this.lastDeltaTime=e,!(this.remainingDelay>0)&&(this.emit(_.BEGIN),this.step=this._stepTime,this.step(Math.abs(this.remainingDelay)),this.remainingDelay=this.settings.delay)});y(this,"_stepTime",(e=0)=>{this.elapsedTime+=e,this.lastDeltaTime=e,this.elapsedTime>=this.time?(this.elapsedTime=this.time,this._update(),this._complete()):this._update()});this.index=J++,this.overrides=e,this.settings={},this.reset(e),i&&(this.manager=i),this.lastDeltaTime=0}on(...e){return super.on(...e),this}off(...e){return super.off(...e),this}once(...e){return super.once(...e),this}onComplete(...e){return super.on(_.COMPLETE,...e),this}onUpdate(...e){return super.on(_.UPDATE,...e),this}onceComplete(...e){return super.once(_.COMPLETE,...e),this}static _noop(){}static mergeConfigs(e,i){e!==i&&Object.keys(i).forEach(r=>{typeof i[r]=="object"&&i[r]!==null?((!e[r]||typeof e[r]!="object")&&(e[r]={}),d.mergeConfigs(e[r],i[r])):e[r]=i[r]})}static lerp(e,i,r){return e+(i-e)*r}_processState(){this.step=d._noop,this.progress=0,this.easeValue=0,this.elapsedTime=0,this.promise=null,this._resolve=d._noop,this._refreshDynamicProps()}_refreshDynamicProps(){this.applyProcessors(this.settings);let{target:e,time:i,repeat:r,loop:n,reversed:a,delay:p}=this.settings;this.target=e,this.time=Math.max(i,0),this.repeat=r>0?r:n?1/0:0,this.reversed=a,this.remainingDelay=p,this._processEasing()}_processEasing(){let e=typeof this.settings.easing=="string"?E[this.settings.easing]||E.linear:this.settings.easing;this._easing=this.reversed?this._reversedEasing:e;let i=typeof this.settings.mode=="string"?O[this.settings.mode]:this.settings.mode;this._calculateEasing=i?i.bind(this):this._easing}_update(){this.progress=this.elapsedTime/this.time,this.easeValue=this._calculateEasing(this.progress),this.emit(_.UPDATE,this)}_complete(){this.repeat-- >0?this._repeat():(this._resolve(),this.stop(!1),this.emit(_.COMPLETE))}_repeat(e=!0){this.remainingDelay=this.settings.repeatDelay,this.elapsedTime=0,this.remainingDelay>0&&(this.step=this._stepDelay),this._update(),e&&this.emit(_.REPEAT)}reset(e=this.constructor.DEFAULTS){return this.settings={...this.constructor.DEFAULTS,...e},this._processState(),this}applyProcessors(e=this.settings){e.processors.forEach(i=>{d.mergeConfigs(e,i.call(this,e)||{})})}tweak(e={}){return d.mergeConfigs(this.settings,e),this._refreshDynamicProps(),this}seek(e=0,i=!0){return this.elapsedTime=Math.min(Math.max(e,0),this.time),i&&this._update(),this}setProgress(e=0,i=!0){return this.seek(this.time*e,i)}reverse(e=!1){return this.reversed=!this.reversed,this.seek(this.time-this.elapsedTime,e),this._processEasing(),this}play(e=!0){return this.isPlaying?this:(this.settings.autoApplyProcessors&&this._refreshDynamicProps(),e&&this.emit(_.PLAY),this.remainingDelay>0?this.step=this._stepDelay:(this.step=this._stepTime,this.emit(_.BEGIN)),this.manager&&this.manager.addToActive(this),this)}playPromise(e=this.promise){return this.play(),this.promise=e||new Promise(i=>{this._resolve=i}),this.promise}stop(e=!0){return this._processState(),e&&this.emit(_.STOP),this.manager&&this.manager.removeFromActive(this),this}pause(){return this.step=d._noop,this.manager&&this.manager.removeFromActive(this),this}replay(e=!1){return this.stop(e),this.play(e),this}get isPlaying(){return this.step!==d._noop}remove(){this.stop(),this.manager.remove(this)}};y(d,"DEFAULTS",C);var u=d;var P=class extends u{_preparePropertySetters(){this.propertySetters=this.properties.map(s=>{let e=s.split("."),i=e.pop();return r=>{let n=this.target;for(let a=0;a<e.length;a++)n=n[e[a]];n[i]=r}})}_generatePath(){let s=this.settings.points,e=Math.max(this.settings.smoothing,1),i=[];this.totalLength=0;function r(n,a,p,o,l){let m=l*l,h=m*l;return n.map((I,c)=>.5*(2*a[c]+(-n[c]+p[c])*l+(2*n[c]-5*a[c]+4*p[c]-o[c])*m+(-n[c]+3*a[c]-3*p[c]+o[c])*h))}for(let n=0;n<s.length-1;n++){let a=s[n===0?n:n-1],p=s[n],o=s[n+1],l=s[n+2<s.length?n+2:n+1];for(let m=0;m<e;m++){let h=r(a,p,o,l,m/e);i.push(h),i.length>1&&(this.totalLength+=this._calculateDistance(i[i.length-2],h))}}return i.push(s[s.length-1]),i}_calculateDistance(s,e){return Math.sqrt(s.reduce((i,r,n)=>i+Math.pow(e[n]-s[n],2),0))}_refreshDynamicProps(){super._refreshDynamicProps(),this.path=this._generatePath(),!this.target||this.path.length===0?this._setTargetProperties=u._noop:(this._tryToSetupProperties(),this._preparePropertySetters())}_tryToSetupProperties(){if(this.settings.properties)this.properties=this.settings.properties;else if(this.path[0].length<=3){let s=[["position.x"],["position.x","position.y"],["position.x","position.y","position.z"]];this.properties=s[this.path[0].length-1]}else this.properties=[]}_setTargetProperties(s){for(let e=0;e<this.propertySetters.length;e++)this.propertySetters[e](s[e])}_clamp(s,e,i){return Math.max(e,Math.min(s,i))}_getInterpolatedPosition(){let s=this.path.length-1,e=this.easeValue*s,i=this._clamp(e,0,s),r=i>=s?s-1:Math.floor(i),n=Math.min(r+1,s),a=e-r,p=this.path[r],o=this.path[n];return p.map((l,m)=>u.lerp(l,o[m],a))}_update(){super._update(),this._setTargetProperties(this._getInterpolatedPosition())}};y(P,"DEFAULTS",{...u.DEFAULTS,properties:null,points:[],smoothing:20});var A=class extends u{_refreshDynamicProps(){super._refreshDynamicProps(),this._processFromTo()}_processFromTo(){this._lerps=[],this.target&&(this._setupStates(),this._createLerps())}_setupStates(){this._from=this.settings.from||this._createState(this.settings.to||{},this.target),this._to=this.settings.to||this._createState(this.settings.from||{},this.target)}_createState(s={},e={},i={}){let r=(n,a,p)=>{for(let o in n)typeof n[o]=="object"?(p[o]={},r(n[o],a[o],p[o])):p[o]=a[o]};return r(s,e,i),i}_createLerpStep(s,e,i,r){if(this.settings.dynamic)return()=>{s[r]=u.lerp(e[r],i[r],this.easeValue)};{let n=e[r],a=i[r]-n;return()=>{s[r]=n+a*this.easeValue}}}_createLerps(s=this.target,e=this._from,i=this._to){for(let r in i)typeof i[r]=="object"?this._createLerps(s[r],e[r],i[r]):this._lerps.push(this._createLerpStep(s,e,i,r))}_update(){super._update();for(let s of this._lerps)s()}from(s={}){return this.tweak({from:s}),this}to(s={}){return this.tweak({to:s}),this}swap(){return this.tweak({from:this.settings.to,to:this.settings.from}),this}};y(A,"DEFAULTS",{...u.DEFAULTS,dynamic:!1,from:null,to:null});function w(t){return t.target?t.creator?t.creator.prototype instanceof u?t.creator:(console.error("Invalid creator provided. Using default creator."),u):t.points?P:t.from||t.to?A:u:u}var S=class{constructor(){y(this,"update",s=>{this.activeAnimations.forEach(e=>{e.step(s)})});this.activeAnimations=new Map,this.allAnimations=new Map}create({on:s={},once:e={},...i}){let r=w(i),n=new r(i,this);return Object.keys(s).forEach(a=>{n.on(a,s[a])}),Object.keys(e).forEach(a=>{n.once(a,e[a])}),this.add(n),n}getActiveAnimations(){return Array.from(this.activeAnimations.values())}getAllAnimations(){return Array.from(this.allAnimations.values())}remove({index:s}={}){s&&(this.activeAnimations.delete(s),this.allAnimations.delete(s))}add(s){this.allAnimations.set(s.index,s)}addToActive(s){this.activeAnimations.set(s.index,s)}removeFromActive(s){this.activeAnimations.delete(s.index)}stopAll(){this.activeAnimations.forEach(s=>{s.stop()})}removeAll(){this.stopAll(),this.activeAnimations.clear(),this.allAnimations.clear()}};var W=new S;var M=class t{constructor(s,e={}){this.callback=s,this.options={maxDeltaTime:100,...e},this.isRunning=!1,this.lastTime=0,this.animationId=null}static start(s,e={}){let i=new t(s,e);return i.start(),i}start(){if(this.isRunning)return;this.isRunning=!0;let s=e=>{if(!this.isRunning)return;let i=Math.min(e-this.lastTime,this.options.maxDeltaTime);this.lastTime=e,this.callback(i),this.isRunning&&(this.animationId=requestAnimationFrame(s))};requestAnimationFrame(e=>{this.lastTime=e,s(e)})}stop(){this.isRunning=!1,this.animationId&&(cancelAnimationFrame(this.animationId),this.animationId=null)}get running(){return this.isRunning}};function X(t,s){let e=new w(t),i=new e(t);return M.start(i.step.bind(i)),s?i.playPromise():i.play()}export{u as Core,P as Curve,C as DEFAULTS,_ as EVENTS,A as FromTo,W as GlobalManager,M as Loop,S as Manager,E as easings,O as modes,X as play};
package/package.json CHANGED
@@ -1,16 +1,25 @@
1
1
  {
2
2
  "name": "qarl",
3
- "version": "1.2.5",
3
+ "version": "1.3.1",
4
4
  "description": "simple animation library",
5
- "main": "dist/qarl.es.min.js",
6
- "module": "dist/qarl.es.min.js",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./types/index.d.ts",
7
9
  "exports": {
8
- ".": "./dist/qarl.es.min.js"
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./types/index.d.ts"
13
+ }
9
14
  },
15
+ "files": [
16
+ "dist",
17
+ "types"
18
+ ],
10
19
  "scripts": {
11
- "build": "webpack --mode=production",
12
- "dev": "node kill-port.js && webpack serve --mode=development",
13
- "publish": "npm run build && npm publish"
20
+ "build": "esbuild src/index.js --bundle --format=esm --outfile=dist/index.js --minify --target=es2020 && npm run type-check",
21
+ "type-check": "tsc --noEmit",
22
+ "prepare": "npm run build"
14
23
  },
15
24
  "repository": {
16
25
  "type": "git",
@@ -26,12 +35,7 @@
26
35
  "eventemitter3": "^5.0.1"
27
36
  },
28
37
  "devDependencies": {
29
- "html-loader": "^5.1.0",
30
- "html-webpack-plugin": "^5.6.4",
31
- "terser-webpack-plugin": "^5.3.14",
32
- "three": "^0.180.0",
33
- "webpack": "^5.102.1",
34
- "webpack-cli": "^6.0.1",
35
- "webpack-dev-server": "^5.2.2"
38
+ "esbuild": "^0.19.0",
39
+ "typescript": "^5.3.0"
36
40
  }
37
41
  }
@@ -0,0 +1,243 @@
1
+ import EventEmitter from 'eventemitter3';
2
+ import { CoreSettings, EasingFunction } from './common';
3
+
4
+ /**
5
+ * Core — base animation class built on EventEmitter3.
6
+ * Manages timing, easing, delays, repeats, and lifecycle events.
7
+ * Extended by FromTo (property interpolation) and Curve (spline animation).
8
+ *
9
+ * @example
10
+ * const anim = new Core({ time: 1000, easing: 'outQuad', loop: true });
11
+ * anim.onUpdate((a) => console.log(a.progress, a.easeValue));
12
+ * anim.play();
13
+ *
14
+ * @example
15
+ * // With promise
16
+ * await anim.playPromise();
17
+ * console.log('animation complete');
18
+ */
19
+ export class Core extends EventEmitter {
20
+ /** Default settings applied to every new instance. */
21
+ static DEFAULTS: CoreSettings;
22
+
23
+ /**
24
+ * Linear interpolation between two values.
25
+ * @param a - Start value
26
+ * @param b - End value
27
+ * @param t - Interpolation factor (0..1)
28
+ * @returns Interpolated value
29
+ */
30
+ static lerp(a: number, b: number, t: number): number;
31
+
32
+ /**
33
+ * Deep-merge `source` into `target`, mutating `target` in place.
34
+ * Nested objects are merged recursively; primitives are overwritten.
35
+ */
36
+ static mergeConfigs(target: object, source: object): void;
37
+
38
+ /** Unique numeric index assigned at construction. */
39
+ index: number;
40
+
41
+ /** Original overrides passed to the constructor. */
42
+ overrides: Partial<CoreSettings>;
43
+
44
+ /** Current resolved settings (defaults merged with overrides). */
45
+ settings: CoreSettings;
46
+
47
+ /** Current animation progress (0..1), before easing. */
48
+ progress: number;
49
+
50
+ /** Current eased value (0..1 for standard easings, can overshoot for elastic/back). */
51
+ easeValue: number;
52
+
53
+ /** Elapsed time in milliseconds since animation started. */
54
+ elapsedTime: number;
55
+
56
+ /** Reference to `settings.target`. */
57
+ target: any;
58
+
59
+ /** Resolved animation duration in milliseconds (≥ 0). */
60
+ time: number;
61
+
62
+ /** Remaining repetitions. */
63
+ repeat: number;
64
+
65
+ /** Whether the animation is currently playing in reverse. */
66
+ reversed: boolean;
67
+
68
+ /** Remaining delay before next step in milliseconds. */
69
+ remainingDelay: number;
70
+
71
+ /** Delta time from the most recent `step()` call. */
72
+ lastDeltaTime: number;
73
+
74
+ /** The Manager this animation belongs to, if any. */
75
+ manager?: import('./Manager').Manager;
76
+
77
+ /**
78
+ * @param overrides - Settings to merge on top of `Core.DEFAULTS`
79
+ * @param manager - Optional Manager instance to auto-register with
80
+ */
81
+ constructor(overrides?: Partial<CoreSettings>, manager?: import('./Manager').Manager);
82
+
83
+ /**
84
+ * Advance the animation by `deltaTime` milliseconds.
85
+ * Called by Manager or Loop each frame.
86
+ * Internally delegates to delay/time step based on current state.
87
+ */
88
+ step(deltaTime: number): void;
89
+
90
+ /**
91
+ * Subscribe to an event. Returns `this` for chaining.
92
+ * @param event - Event name: `'play'` | `'stop'` | `'begin'` | `'update'` | `'repeat'` | `'complete'`
93
+ * @param fn - Callback
94
+ * @param context - Optional `this` context for the callback
95
+ */
96
+ on(event: string, fn: (...args: any[]) => void, context?: any): this;
97
+
98
+ /**
99
+ * Unsubscribe from an event. Returns `this` for chaining.
100
+ * @param event - Event name
101
+ * @param fn - The exact function reference that was passed to `.on()`
102
+ * @param context - The context that was passed to `.on()`
103
+ */
104
+ off(event: string, fn: (...args: any[]) => void, context?: any): this;
105
+
106
+ /**
107
+ * Subscribe to an event, auto-removed after first fire. Returns `this` for chaining.
108
+ * @param event - Event name
109
+ * @param fn - Callback
110
+ * @param context - Optional `this` context for the callback
111
+ */
112
+ once(event: string, fn: (...args: any[]) => void, context?: any): this;
113
+
114
+ /**
115
+ * Shorthand for `.on('complete', fn)`. Returns `this` for chaining.
116
+ *
117
+ * @example
118
+ * animation.onComplete(() => console.log('done')).play();
119
+ */
120
+ onComplete(fn: (animation: this) => void, context?: any): this;
121
+
122
+ /**
123
+ * Shorthand for `.on('update', fn)`. Returns `this` for chaining.
124
+ *
125
+ * @example
126
+ * animation.onUpdate((a) => {
127
+ * console.log(a.progress, a.easeValue);
128
+ * });
129
+ */
130
+ onUpdate(fn: (animation: this) => void, context?: any): this;
131
+
132
+ /**
133
+ * Shorthand for `.once('complete', fn)`. Returns `this` for chaining.
134
+ * Callback fires once on the next complete, then is removed.
135
+ */
136
+ onceComplete(fn: (animation: this) => void, context?: any): this;
137
+
138
+ /**
139
+ * Reset the animation to its initial state.
140
+ * Merges `newSettings` (or DEFAULTS) and re-initializes all internal state.
141
+ * @param newSettings - New settings to apply (defaults to `DEFAULTS`)
142
+ * @returns `this` for chaining
143
+ */
144
+ reset(newSettings?: Partial<CoreSettings>): this;
145
+
146
+ /**
147
+ * Manually apply all processor functions to the current settings.
148
+ * Called automatically if `autoApplyProcessors` is `true`.
149
+ */
150
+ applyProcessors(settings?: CoreSettings): void;
151
+
152
+ /**
153
+ * Modify settings on a live animation without resetting progress.
154
+ * Deep-merges `newSettings` into current `settings` and refreshes dynamic props.
155
+ *
156
+ * @example
157
+ * animation.tweak({ time: 500, easing: 'inOutCubic' });
158
+ *
159
+ * @returns `this` for chaining
160
+ */
161
+ tweak(newSettings?: Partial<CoreSettings>): this;
162
+
163
+ /**
164
+ * Jump to a specific time in the animation.
165
+ * @param time - Target time in milliseconds, clamped to [0, this.time]
166
+ * @param callUpdate - Whether to fire the UPDATE event (default: `true`)
167
+ * @returns `this` for chaining
168
+ */
169
+ seek(time?: number, callUpdate?: boolean): this;
170
+
171
+ /**
172
+ * Jump to a specific progress value.
173
+ * @param progress - Target progress (0..1)
174
+ * @param callUpdate - Whether to fire the UPDATE event (default: `true`)
175
+ * @returns `this` for chaining
176
+ */
177
+ setProgress(progress?: number, callUpdate?: boolean): this;
178
+
179
+ /**
180
+ * Toggle animation direction. Flips `reversed` and adjusts `elapsedTime`
181
+ * so the animation continues from the mirrored position.
182
+ * @param callUpdate - Whether to fire the UPDATE event (default: `false`)
183
+ * @returns `this` for chaining
184
+ */
185
+ reverse(callUpdate?: boolean): this;
186
+
187
+ /**
188
+ * Start the animation.
189
+ * If a delay is set, transitions to delay state first (PLAY fires immediately, BEGIN fires after delay).
190
+ * No-op if already playing.
191
+ * @param withEvent - Whether to emit the PLAY event (default: `true`)
192
+ * @returns `this` for chaining
193
+ *
194
+ * @example
195
+ * animation.play();
196
+ */
197
+ play(withEvent?: boolean): this;
198
+
199
+ /**
200
+ * Start the animation and return a Promise that resolves on complete.
201
+ *
202
+ * @example
203
+ * await animation.playPromise();
204
+ * // animation finished
205
+ *
206
+ * @param promise - Optional existing promise to reuse
207
+ * @returns Promise that resolves when the animation completes
208
+ */
209
+ playPromise(promise?: Promise<void>): Promise<void>;
210
+
211
+ /**
212
+ * Stop the animation, reset internal state (`progress`, `elapsedTime`, etc.).
213
+ * Removes from Manager's active list.
214
+ * @param withEvent - Whether to emit the STOP event (default: `true`)
215
+ * @returns `this` for chaining
216
+ */
217
+ stop(withEvent?: boolean): this;
218
+
219
+ /**
220
+ * Pause the animation at its current position.
221
+ * Call `.play()` to resume from the paused point.
222
+ * @returns `this` for chaining
223
+ */
224
+ pause(): this;
225
+
226
+ /**
227
+ * Stop and immediately play again from the beginning.
228
+ * @param withEvent - Whether to emit STOP/PLAY events (default: `false`)
229
+ * @returns `this` for chaining
230
+ */
231
+ replay(withEvent?: boolean): this;
232
+
233
+ /**
234
+ * Remove this animation from its Manager (both active and all lists).
235
+ * Calls `.stop()` first.
236
+ */
237
+ remove(): void;
238
+
239
+ /**
240
+ * `true` if the animation is currently advancing (not paused/stopped).
241
+ */
242
+ get isPlaying(): boolean;
243
+ }
@@ -0,0 +1,61 @@
1
+ import { Core } from './Core';
2
+ import { CurveSettings } from './common';
3
+
4
+ /**
5
+ * Curve — animates target properties along a Catmull-Rom spline defined by keypoints.
6
+ *
7
+ * Auto-detects property mapping for 1D–3D points (`position.x`, `.y`, `.z`).
8
+ * For higher dimensions or custom paths, provide `properties` explicitly.
9
+ *
10
+ * @example
11
+ * // 3D path — properties auto-detected as position.x/y/z
12
+ * const anim = new Curve({
13
+ * target: mesh,
14
+ * time: 2000,
15
+ * points: [
16
+ * [0, 0, 0],
17
+ * [5, 10, 0],
18
+ * [10, 0, 5],
19
+ * [0, 0, 0],
20
+ * ],
21
+ * easing: 'inOutSine',
22
+ * loop: true,
23
+ * });
24
+ * anim.play();
25
+ *
26
+ * @example
27
+ * // Custom properties — 6D points controlling position + scale
28
+ * const anim = new Curve({
29
+ * target: mesh,
30
+ * time: 3000,
31
+ * properties: ['position.x', 'position.y', 'position.z', 'scale.x', 'scale.y', 'scale.z'],
32
+ * points: [
33
+ * [0, 0, 0, 1, 1, 1],
34
+ * [5, 10, 0, 2, 0.5, 2],
35
+ * [0, 0, 0, 1, 1, 1],
36
+ * ],
37
+ * smoothing: 30,
38
+ * });
39
+ */
40
+ export class Curve extends Core {
41
+ /** Default settings for Curve, extending Core.DEFAULTS with `points`, `properties`, `smoothing`. */
42
+ static DEFAULTS: CurveSettings;
43
+
44
+ /** Current resolved settings. */
45
+ settings: CurveSettings;
46
+
47
+ /** Generated spline path — array of interpolated points. */
48
+ path: number[][];
49
+
50
+ /** Total arc length of the generated path. */
51
+ totalLength: number;
52
+
53
+ /** Resolved property paths assigned from interpolated values each frame. */
54
+ properties: string[];
55
+
56
+ /**
57
+ * @param overrides - Settings to merge on top of `Curve.DEFAULTS`
58
+ * @param manager - Optional Manager instance to auto-register with
59
+ */
60
+ constructor(overrides?: Partial<CurveSettings>, manager?: import('./Manager').Manager);
61
+ }
@@ -0,0 +1,87 @@
1
+ import { Core } from './Core';
2
+ import { FromToSettings } from './common';
3
+
4
+ /**
5
+ * FromTo — animates target properties by interpolating between `from` and `to` values.
6
+ * Automatically detects numeric properties (including nested objects) and creates lerps.
7
+ *
8
+ * If only `from` is provided, current target values are used as `to` (and vice versa).
9
+ *
10
+ * @example
11
+ * const anim = new FromTo({
12
+ * target: mesh.position,
13
+ * from: { x: 0, y: 0 },
14
+ * to: { x: 100, y: 200 },
15
+ * time: 1000,
16
+ * easing: 'outQuad',
17
+ * });
18
+ * anim.play();
19
+ *
20
+ * @example
21
+ * // Nested properties
22
+ * const anim = new FromTo({
23
+ * target: mesh,
24
+ * from: { position: { x: 0 }, scale: { x: 1 } },
25
+ * to: { position: { x: 10 }, scale: { x: 2 } },
26
+ * time: 500,
27
+ * });
28
+ *
29
+ * @example
30
+ * // Dynamic mode — from/to are re-read each frame
31
+ * const anim = new FromTo({
32
+ * target: obj,
33
+ * from: startValues,
34
+ * to: endValues,
35
+ * dynamic: true,
36
+ * time: 1000,
37
+ * });
38
+ * // Changing startValues/endValues during animation takes effect immediately
39
+ */
40
+ export class FromTo extends Core {
41
+ /** Default settings for FromTo, extending Core.DEFAULTS with `from`, `to`, `dynamic`. */
42
+ static DEFAULTS: FromToSettings;
43
+
44
+ /** Current resolved settings. */
45
+ settings: FromToSettings;
46
+
47
+ /**
48
+ * @param overrides - Settings to merge on top of `FromTo.DEFAULTS`
49
+ * @param manager - Optional Manager instance to auto-register with
50
+ */
51
+ constructor(overrides?: Partial<FromToSettings>, manager?: import('./Manager').Manager);
52
+
53
+ /**
54
+ * Update `from` values on a live animation.
55
+ * Calls `.tweak()` internally and rebuilds interpolation.
56
+ *
57
+ * @example
58
+ * animation.from({ x: 50 }).play();
59
+ *
60
+ * @param from - New starting values
61
+ * @returns `this` for chaining
62
+ */
63
+ from(from?: Record<string, any>): this;
64
+
65
+ /**
66
+ * Update `to` values on a live animation.
67
+ * Calls `.tweak()` internally and rebuilds interpolation.
68
+ *
69
+ * @example
70
+ * animation.to({ x: 200 }).play();
71
+ *
72
+ * @param to - New ending values
73
+ * @returns `this` for chaining
74
+ */
75
+ to(to?: Record<string, any>): this;
76
+
77
+ /**
78
+ * Swap `from` and `to` values, effectively reversing the interpolation direction.
79
+ * Unlike `.reverse()`, this swaps the actual values rather than inverting the easing.
80
+ *
81
+ * @example
82
+ * animation.swap().replay();
83
+ *
84
+ * @returns `this` for chaining
85
+ */
86
+ swap(): this;
87
+ }
@@ -0,0 +1,65 @@
1
+ import { LoopOptions } from './common';
2
+
3
+ /**
4
+ * Loop — requestAnimationFrame wrapper with delta-time capping.
5
+ * Calls the provided callback every frame with `dt` in milliseconds.
6
+ *
7
+ * @example
8
+ * // Static shorthand — create and start in one call
9
+ * const loop = Loop.start((dt) => {
10
+ * manager.update(dt);
11
+ * });
12
+ *
13
+ * // Stop later
14
+ * loop.stop();
15
+ *
16
+ * @example
17
+ * // Manual lifecycle
18
+ * const loop = new Loop((dt) => console.log(dt), { maxDeltaTime: 50 });
19
+ * loop.start();
20
+ * // ...
21
+ * loop.stop();
22
+ */
23
+ export class Loop {
24
+ /** The callback invoked each frame with `dt` (capped by `maxDeltaTime`). */
25
+ callback: (dt: number) => void;
26
+
27
+ /** Resolved options with defaults applied. */
28
+ options: Required<LoopOptions>;
29
+
30
+ /** Whether the loop is currently running. */
31
+ isRunning: boolean;
32
+
33
+ /** Timestamp of the previous frame (from `requestAnimationFrame`). */
34
+ lastTime: number;
35
+
36
+ /** Current `requestAnimationFrame` handle, or `null` if stopped. */
37
+ animationId: number | null;
38
+
39
+ /**
40
+ * @param callback - Function called every frame with delta time in ms
41
+ * @param options - Loop options
42
+ */
43
+ constructor(callback: (dt: number) => void, options?: LoopOptions);
44
+
45
+ /**
46
+ * Create a Loop and immediately start it.
47
+ *
48
+ * @example
49
+ * const loop = Loop.start(manager.update);
50
+ *
51
+ * @param callback - Function called every frame with delta time in ms
52
+ * @param options - Loop options
53
+ * @returns The running Loop instance
54
+ */
55
+ static start(callback: (dt: number) => void, options?: LoopOptions): Loop;
56
+
57
+ /** Start the loop. No-op if already running. */
58
+ start(): void;
59
+
60
+ /** Stop the loop and cancel the next animation frame. */
61
+ stop(): void;
62
+
63
+ /** Alias for `isRunning`. */
64
+ get running(): boolean;
65
+ }
@@ -0,0 +1,121 @@
1
+ import { Core } from './Core';
2
+ import { ManagerCreateConfig } from './common';
3
+
4
+ /**
5
+ * Manager — creates, stores, and batch-updates animations.
6
+ * Animations created via `.create()` are automatically registered in the Manager.
7
+ * Call `.update(dt)` each frame (typically from a Loop) to advance all active animations.
8
+ *
9
+ * @example
10
+ * const manager = new Manager();
11
+ *
12
+ * const anim = manager.create({
13
+ * target: mesh.position,
14
+ * from: { x: 0 },
15
+ * to: { x: 100 },
16
+ * time: 1000,
17
+ * on: { complete: () => console.log('done') },
18
+ * });
19
+ *
20
+ * anim.play();
21
+ * Loop.start(manager.update);
22
+ */
23
+ export class Manager {
24
+ /** Map of currently playing animations (index → animation). */
25
+ activeAnimations: Map<number, Core>;
26
+
27
+ /** Map of all registered animations, active or not (index → animation). */
28
+ allAnimations: Map<number, Core>;
29
+
30
+ /**
31
+ * Create a new animation, auto-detecting the type (Core, FromTo, or Curve).
32
+ * The animation is registered in the Manager but not started — call `.play()`.
33
+ *
34
+ * Type detection:
35
+ * - `config.points` → Curve
36
+ * - `config.from` or `config.to` → FromTo
37
+ * - otherwise → Core
38
+ * - `config.creator` → custom class (must extend Core)
39
+ *
40
+ * Event binding shortcuts:
41
+ * - `config.on` — persistent listeners (`.on()`)
42
+ * - `config.once` — one-time listeners (`.once()`)
43
+ *
44
+ * @example
45
+ * const anim = manager.create({
46
+ * target: obj,
47
+ * to: { x: 10 },
48
+ * time: 500,
49
+ * easing: 'outQuad',
50
+ * on: { update: (a) => render(a.target) },
51
+ * });
52
+ * anim.play();
53
+ */
54
+ create(config: ManagerCreateConfig): Core;
55
+
56
+ /**
57
+ * Advance all active animations by `dt` milliseconds.
58
+ * Bound to the instance — safe to pass directly to `Loop.start()`.
59
+ *
60
+ * @example
61
+ * Loop.start(manager.update);
62
+ */
63
+ update(dt: number): void;
64
+
65
+ /**
66
+ * Get a snapshot array of all currently playing animations.
67
+ */
68
+ getActiveAnimations(): Core[];
69
+
70
+ /**
71
+ * Get a snapshot array of all registered animations (active + inactive).
72
+ */
73
+ getAllAnimations(): Core[];
74
+
75
+ /**
76
+ * Remove an animation from both active and all lists by its index.
77
+ * @param animation - Object with an `index` property
78
+ */
79
+ remove(animation: { index?: number }): void;
80
+
81
+ /**
82
+ * Register an animation in the `allAnimations` map.
83
+ * Called automatically by `.create()`.
84
+ */
85
+ add(animation: Core): void;
86
+
87
+ /**
88
+ * Move an animation to the active list (it will receive `.update()` calls).
89
+ * Called automatically by `animation.play()` when a Manager is attached.
90
+ */
91
+ addToActive(animation: Core): void;
92
+
93
+ /**
94
+ * Remove an animation from the active list (stops receiving updates).
95
+ * Called automatically by `animation.stop()` / `.pause()`.
96
+ */
97
+ removeFromActive(animation: Core): void;
98
+
99
+ /**
100
+ * Stop all active animations (calls `.stop()` on each).
101
+ */
102
+ stopAll(): void;
103
+
104
+ /**
105
+ * Stop all animations and clear both active and all lists.
106
+ */
107
+ removeAll(): void;
108
+ }
109
+
110
+ /**
111
+ * Pre-created global Manager singleton.
112
+ * Convenient when you don't need multiple independent animation groups.
113
+ *
114
+ * @example
115
+ * import { GlobalManager, Loop } from 'qarl';
116
+ *
117
+ * const anim = GlobalManager.create({ target: obj, to: { x: 10 }, time: 500 });
118
+ * anim.play();
119
+ * Loop.start(GlobalManager.update);
120
+ */
121
+ export const GlobalManager: Manager;