@thednp/tween 0.0.1 → 0.0.3

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.
Files changed (53) hide show
  1. package/README.md +112 -109
  2. package/dist/preact/preact.d.mts +62 -0
  3. package/dist/preact/preact.d.mts.map +1 -0
  4. package/dist/preact/preact.mjs +181 -0
  5. package/dist/preact/preact.mjs.map +1 -0
  6. package/dist/react/react.d.mts +62 -0
  7. package/dist/react/react.d.mts.map +1 -0
  8. package/dist/react/react.mjs +183 -0
  9. package/dist/react/react.mjs.map +1 -0
  10. package/dist/solid/solid.d.mts +60 -0
  11. package/dist/solid/solid.d.mts.map +1 -0
  12. package/dist/solid/solid.mjs +157 -0
  13. package/dist/solid/solid.mjs.map +1 -0
  14. package/dist/svelte/svelte.d.mts.map +1 -0
  15. package/dist/svelte/svelte.mjs.map +1 -0
  16. package/dist/svelte/tween.svelte.d.ts +58 -0
  17. package/dist/svelte/tween.svelte.js +156 -0
  18. package/dist/tween/index.d.mts +939 -0
  19. package/dist/tween/index.d.mts.map +1 -0
  20. package/dist/tween/index.mjs +1929 -0
  21. package/dist/tween/index.mjs.map +1 -0
  22. package/dist/tween.min.js +8 -0
  23. package/dist/tween.min.js.map +1 -0
  24. package/dist/vue/vue.d.mts +61 -0
  25. package/dist/vue/vue.d.mts.map +1 -0
  26. package/dist/vue/vue.mjs +157 -0
  27. package/dist/vue/vue.mjs.map +1 -0
  28. package/package.json +49 -40
  29. package/dist/index.cjs +0 -621
  30. package/dist/index.cjs.map +0 -1
  31. package/dist/index.d.cts +0 -184
  32. package/dist/index.d.mts +0 -184
  33. package/dist/index.mjs +0 -610
  34. package/dist/index.mjs.map +0 -1
  35. package/dist/react.cjs +0 -61
  36. package/dist/react.cjs.map +0 -1
  37. package/dist/react.d.cts +0 -9
  38. package/dist/react.d.mts +0 -9
  39. package/dist/react.mjs +0 -55
  40. package/dist/react.mjs.map +0 -1
  41. package/dist/solid.cjs +0 -81
  42. package/dist/solid.cjs.map +0 -1
  43. package/dist/solid.d.cts +0 -9
  44. package/dist/solid.d.mts +0 -9
  45. package/dist/solid.mjs +0 -75
  46. package/dist/solid.mjs.map +0 -1
  47. package/dist/tween.iife.js +0 -2
  48. package/dist/tween.iife.js.map +0 -1
  49. package/wiki/Easing.md +0 -58
  50. package/wiki/React.md +0 -255
  51. package/wiki/Solid.md +0 -149
  52. package/wiki/Timeline.md +0 -207
  53. package/wiki/Tween.md +0 -230
@@ -0,0 +1,1929 @@
1
+ /*!
2
+ * @thednp/tween v0.0.3 (https://github.com/thednp/tween)
3
+ * Copyright 2026 © thednp
4
+ * Licensed under MIT (https://github.com/thednp/tween/blob/master/LICENSE)
5
+ */
6
+ "use strict";
7
+
8
+ //#region src/Easing.ts
9
+ /**
10
+ * The Ease class provides a collection of easing functions for use with tween.js.
11
+ */
12
+ const Easing = Object.freeze({
13
+ Linear: Object.freeze({
14
+ None(amount) {
15
+ return amount;
16
+ },
17
+ In(amount) {
18
+ return amount;
19
+ },
20
+ Out(amount) {
21
+ return amount;
22
+ },
23
+ InOut(amount) {
24
+ return amount;
25
+ }
26
+ }),
27
+ Quadratic: Object.freeze({
28
+ In(amount) {
29
+ return amount * amount;
30
+ },
31
+ Out(amount) {
32
+ return amount * (2 - amount);
33
+ },
34
+ InOut(amount) {
35
+ if ((amount *= 2) < 1) return .5 * amount * amount;
36
+ return -.5 * (--amount * (amount - 2) - 1);
37
+ }
38
+ }),
39
+ Cubic: Object.freeze({
40
+ In(amount) {
41
+ return amount * amount * amount;
42
+ },
43
+ Out(amount) {
44
+ return --amount * amount * amount + 1;
45
+ },
46
+ InOut(amount) {
47
+ if ((amount *= 2) < 1) return .5 * amount * amount * amount;
48
+ return .5 * ((amount -= 2) * amount * amount + 2);
49
+ }
50
+ }),
51
+ Quartic: Object.freeze({
52
+ In(amount) {
53
+ return amount * amount * amount * amount;
54
+ },
55
+ Out(amount) {
56
+ return 1 - --amount * amount * amount * amount;
57
+ },
58
+ InOut(amount) {
59
+ if ((amount *= 2) < 1) return .5 * amount * amount * amount * amount;
60
+ return -.5 * ((amount -= 2) * amount * amount * amount - 2);
61
+ }
62
+ }),
63
+ Quintic: Object.freeze({
64
+ In(amount) {
65
+ return amount * amount * amount * amount * amount;
66
+ },
67
+ Out(amount) {
68
+ return --amount * amount * amount * amount * amount + 1;
69
+ },
70
+ InOut(amount) {
71
+ if ((amount *= 2) < 1) return .5 * amount * amount * amount * amount * amount;
72
+ return .5 * ((amount -= 2) * amount * amount * amount * amount + 2);
73
+ }
74
+ }),
75
+ Sinusoidal: Object.freeze({
76
+ In(amount) {
77
+ return 1 - Math.sin((1 - amount) * Math.PI / 2);
78
+ },
79
+ Out(amount) {
80
+ return Math.sin(amount * Math.PI / 2);
81
+ },
82
+ InOut(amount) {
83
+ return .5 * (1 - Math.sin(Math.PI * (.5 - amount)));
84
+ }
85
+ }),
86
+ Exponential: Object.freeze({
87
+ In(amount) {
88
+ return amount === 0 ? 0 : Math.pow(1024, amount - 1);
89
+ },
90
+ Out(amount) {
91
+ return amount === 1 ? 1 : 1 - Math.pow(2, -10 * amount);
92
+ },
93
+ InOut(amount) {
94
+ if (amount === 0) return 0;
95
+ if (amount === 1) return 1;
96
+ if ((amount *= 2) < 1) return .5 * Math.pow(1024, amount - 1);
97
+ return .5 * (-Math.pow(2, -10 * (amount - 1)) + 2);
98
+ }
99
+ }),
100
+ Circular: Object.freeze({
101
+ In(amount) {
102
+ return 1 - Math.sqrt(1 - amount * amount);
103
+ },
104
+ Out(amount) {
105
+ return Math.sqrt(1 - --amount * amount);
106
+ },
107
+ InOut(amount) {
108
+ if ((amount *= 2) < 1) return -.5 * (Math.sqrt(1 - amount * amount) - 1);
109
+ return .5 * (Math.sqrt(1 - (amount -= 2) * amount) + 1);
110
+ }
111
+ }),
112
+ Elastic: Object.freeze({
113
+ In(amount) {
114
+ if (amount === 0) return 0;
115
+ if (amount === 1) return 1;
116
+ return -Math.pow(2, 10 * (amount - 1)) * Math.sin((amount - 1.1) * 5 * Math.PI);
117
+ },
118
+ Out(amount) {
119
+ if (amount === 0) return 0;
120
+ if (amount === 1) return 1;
121
+ return Math.pow(2, -10 * amount) * Math.sin((amount - .1) * 5 * Math.PI) + 1;
122
+ },
123
+ InOut(amount) {
124
+ if (amount === 0) return 0;
125
+ if (amount === 1) return 1;
126
+ amount *= 2;
127
+ if (amount < 1) return -.5 * Math.pow(2, 10 * (amount - 1)) * Math.sin((amount - 1.1) * 5 * Math.PI);
128
+ return .5 * Math.pow(2, -10 * (amount - 1)) * Math.sin((amount - 1.1) * 5 * Math.PI) + 1;
129
+ }
130
+ }),
131
+ Back: Object.freeze({
132
+ In(amount) {
133
+ const s = 1.70158;
134
+ return amount === 1 ? 1 : amount * amount * ((s + 1) * amount - s);
135
+ },
136
+ Out(amount) {
137
+ const s = 1.70158;
138
+ return amount === 0 ? 0 : --amount * amount * ((s + 1) * amount + s) + 1;
139
+ },
140
+ InOut(amount) {
141
+ const s = 1.70158 * 1.525;
142
+ if ((amount *= 2) < 1) return .5 * (amount * amount * ((s + 1) * amount - s));
143
+ return .5 * ((amount -= 2) * amount * ((s + 1) * amount + s) + 2);
144
+ }
145
+ }),
146
+ Bounce: Object.freeze({
147
+ In(amount) {
148
+ return 1 - Easing.Bounce.Out(1 - amount);
149
+ },
150
+ Out(amount) {
151
+ if (amount < 1 / 2.75) return 7.5625 * amount * amount;
152
+ else if (amount < 2 / 2.75) return 7.5625 * (amount -= 1.5 / 2.75) * amount + .75;
153
+ else if (amount < 2.5 / 2.75) return 7.5625 * (amount -= 2.25 / 2.75) * amount + .9375;
154
+ else return 7.5625 * (amount -= 2.625 / 2.75) * amount + .984375;
155
+ },
156
+ InOut(amount) {
157
+ if (amount < .5) return Easing.Bounce.In(amount * 2) * .5;
158
+ return Easing.Bounce.Out(amount * 2 - 1) * .5 + .5;
159
+ }
160
+ }),
161
+ pow(power = 4) {
162
+ power = power < Number.EPSILON ? Number.EPSILON : power;
163
+ power = power > 1e4 ? 1e4 : power;
164
+ return {
165
+ In(amount) {
166
+ return amount ** power;
167
+ },
168
+ Out(amount) {
169
+ return 1 - (1 - amount) ** power;
170
+ },
171
+ InOut(amount) {
172
+ if (amount < .5) return (amount * 2) ** power / 2;
173
+ return (1 - (2 - amount * 2) ** power) / 2 + .5;
174
+ }
175
+ };
176
+ }
177
+ });
178
+
179
+ //#endregion
180
+ //#region src/Util.ts
181
+ const isString = (value) => typeof value === "string";
182
+ const isNumber = (value) => typeof value === "number";
183
+ const isArray = (value) => Array.isArray(value);
184
+ const isFunction = (value) => typeof value === "function";
185
+ const isObject = (value) => value !== null && value !== void 0 && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype;
186
+ const isPlainObject = (value) => isObject(value) && !isArray(value);
187
+ const isDeepObject = (value) => isPlainObject(value) && Object.values(value).some(isPlainObject);
188
+ const isServer = typeof window === "undefined";
189
+ const instanceMethods = [
190
+ "play",
191
+ "label",
192
+ "start",
193
+ "stop",
194
+ "pause",
195
+ "resume",
196
+ "reverse",
197
+ "use",
198
+ "clear",
199
+ "from",
200
+ "to",
201
+ "easing",
202
+ "delay",
203
+ "yoyo",
204
+ "repeat",
205
+ "update",
206
+ "repeatDelay",
207
+ "onStart",
208
+ "onUpdate",
209
+ "onComplete",
210
+ "onStop",
211
+ "onRepeat"
212
+ ];
213
+ /**
214
+ * SSR helper to speed up UI frameworks render.
215
+ *
216
+ * Why:
217
+ * - skip validation
218
+ * - skip ministore creation
219
+ * - allow free-form configuration for signal based frameworks
220
+ */
221
+ const dummyInstance = {};
222
+ const dummyMethod = () => dummyInstance;
223
+ for (let i = 0; i < instanceMethods.length; i++) dummyInstance[instanceMethods[i]] = dummyMethod;
224
+ /**
225
+ * Utility to round numbers to a specified number of decimals.
226
+ * @param n Input number value
227
+ * @param round Number of decimals
228
+ * @returns The rounded number
229
+ */
230
+ const roundTo = (n, round) => {
231
+ const pow = round >= 1 ? 10 ** round : 1;
232
+ return round > 0 ? Math.round(n * pow) / pow : Math.round(n);
233
+ };
234
+ const objectHasProp = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
235
+ /**
236
+ * A small utility to deep assign up to one level deep nested objects.
237
+ * This is to prevent breaking reactivity of miniStore.
238
+ *
239
+ * **NOTE** - This doesn't perform ANY check and expects objects to
240
+ * be validated beforehand.
241
+ * @param target The target to assign values to
242
+ * @param source The source object to assign values from
243
+ */
244
+ function deepAssign(target, source) {
245
+ const keys = Object.keys(source);
246
+ let i = 0;
247
+ const len = keys.length;
248
+ while (i < len) {
249
+ const key = keys[i++];
250
+ if (key === "__proto__" || key === "constructor" || !objectHasProp(source, key)) continue;
251
+ const targetVal = target[key];
252
+ const sourceVal = source[key];
253
+ if (isArray(sourceVal)) {
254
+ const targetArr = targetVal;
255
+ let j = 0;
256
+ const arLen = sourceVal.length;
257
+ while (j < arLen) {
258
+ const sourceItem = sourceVal[j];
259
+ if (isArray(sourceItem)) {
260
+ const targetItem = targetArr[j];
261
+ let k = 0;
262
+ const itemLen = sourceItem.length;
263
+ while (k < itemLen) {
264
+ targetItem[k] = sourceItem[k];
265
+ k++;
266
+ }
267
+ } else targetArr[j] = sourceItem;
268
+ j++;
269
+ }
270
+ } else if (objectHasProp(target, key) && isObject(sourceVal)) deepAssign(targetVal, sourceVal);
271
+ else target[key] = sourceVal;
272
+ }
273
+ }
274
+ /**
275
+ * Creates a clone of a target object / array without its
276
+ * proxy elements / properties, only their values.
277
+ *
278
+ * **NOTE** - The utility is useful to create deep clones as well.
279
+ * @param value An object / array with proxy elements
280
+ * @returns the object / array value without proxy elements
281
+ */
282
+ const deproxy = (value) => {
283
+ if (isArray(value)) return value.map(deproxy);
284
+ if (isPlainObject(value)) {
285
+ const result = {};
286
+ for (const key in value) if (Object.prototype.hasOwnProperty.call(value, key)) result[key] = deproxy(value[key]);
287
+ return result;
288
+ }
289
+ return value;
290
+ };
291
+ /**
292
+ * Test values validity or their compatibility with the validated ones
293
+ * in the state. This is something we don't want to do in the runtime
294
+ * update loop.
295
+ * @param this The Tween/Timeline instance
296
+ * @param target The target object to validate
297
+ * @param reference The reference state value
298
+ * @returns void
299
+ */
300
+ function validateValues(target, reference) {
301
+ const errors = this.getErrors();
302
+ if (!isPlainObject(target) || Object.keys(target).length === 0) {
303
+ errors.set("init", "Initialization value is empty or not an object.");
304
+ return;
305
+ }
306
+ const keys = Object.keys(target);
307
+ if (reference && keys.some((key) => errors.has(key))) return;
308
+ let i = 0;
309
+ while (i < keys.length) {
310
+ const key = keys[i++];
311
+ const refValue = reference?.[key];
312
+ const value = target[key];
313
+ if (isNumber(value)) continue;
314
+ if (value === void 0 || value === null) {
315
+ errors.set(key, `Property "${key}" is null/undefined.`);
316
+ continue;
317
+ }
318
+ if (reference && refValue === void 0) {
319
+ errors.set(key, `Property "${key}" doesn't exist in state yet.`);
320
+ continue;
321
+ }
322
+ const validator = this.getValidator(key);
323
+ if (validator) {
324
+ const [valid, reason] = validator(key, value, refValue);
325
+ if (valid) errors.delete(key);
326
+ else errors.set(key, reason);
327
+ continue;
328
+ }
329
+ if (reference && isNumber(refValue)) {
330
+ if (!isNumber(value)) errors.set(key, `Property "${key}" is not a number.`);
331
+ continue;
332
+ }
333
+ errors.set(key, `Property "${key}" of type "${isArray(value) ? "array" : typeof value}" is not supported yet.`);
334
+ }
335
+ errors.delete("init");
336
+ }
337
+
338
+ //#endregion
339
+ //#region src/extend/array.ts
340
+ /**
341
+ * Interpolates two `Array<number>` values.
342
+ *
343
+ * **NOTE**: Values my be validated first!
344
+ * @param target The target `Array<number>` value
345
+ * @param start The start `Array<number>` value
346
+ * @param end The end `Array<number>` value
347
+ * @param t The progress value
348
+ * @returns The interpolated `Array<number>` value.
349
+ */
350
+ const interpolateArray = (target, start, end, t) => {
351
+ const len = end.length;
352
+ let i = 0;
353
+ while (i < len) {
354
+ target[i] = start[i] + (end[i] - start[i]) * t;
355
+ i += 1;
356
+ }
357
+ return target;
358
+ };
359
+ /**
360
+ * Check if a value is a valid array for interpolation
361
+ * @param target The array to check
362
+ * @returns `true` is value is array and all elements are numbers
363
+ */
364
+ const isValidArray = (target) => isArray(target) && target.every(isNumber);
365
+ /**
366
+ * Check if an array of numbers is compatible with a reference
367
+ * @param target The incoming value `from()` / `to()`
368
+ * @param ref The state reference value
369
+ * @returns [boolean, reason] tuple when arrays are compatible or
370
+ */
371
+ const validateArray = (propName, target, ref) => {
372
+ if (!isArray(target)) return [false, `Property "${String(propName)}" is not Array.`];
373
+ if (!isValidArray(target)) return [false, `Property "${String(propName)}" is not a valid Array<number>.`];
374
+ if (ref && ref.length !== target.length) return [false, `Property "${String(propName)}" is expecting an array of ${ref.length} numbers.`];
375
+ return [true];
376
+ };
377
+ /**
378
+ * Config for .use(propName, arrayConfig)
379
+ */
380
+ const arrayConfig = {
381
+ interpolate: interpolateArray,
382
+ validate: validateArray
383
+ };
384
+
385
+ //#endregion
386
+ //#region src/extend/path.ts
387
+ /**
388
+ * Iterates a `PathArray` and concatenates the values into a string to return it.
389
+ * **NOTE**: Segment values are rounded to 4 decimals by default.
390
+ * @param path A source PathArray
391
+ * @param round An optional parameter to round segment values to a number of decimals
392
+ * @returns A path string
393
+ */
394
+ function pathToString(path, round = 4) {
395
+ const pathLen = path.length;
396
+ let segment = path[0];
397
+ let result = "";
398
+ let i = 0;
399
+ let segLen = 0;
400
+ while (i < pathLen) {
401
+ segment = path[i++];
402
+ segLen = segment.length;
403
+ result += segment[0];
404
+ let j = 1;
405
+ while (j < segLen) {
406
+ result += roundTo(segment[j++], round);
407
+ if (j !== segLen) result += " ";
408
+ }
409
+ }
410
+ return result;
411
+ }
412
+ /**
413
+ * Interpolate PathArray values.
414
+ * **NOTE**: these values must be validated first! Check validatePath for more info.
415
+ * @param target - The target PathArray value
416
+ * @param start - A starting PathArray value
417
+ * @param end - An ending PathArray value
418
+ * @param t - The progress value
419
+ * @returns The interpolated PathArray value
420
+ */
421
+ const interpolatePath = (target, start, end, t) => {
422
+ const segCount = end.length;
423
+ let i = 0;
424
+ while (i < segCount) {
425
+ const targetSeg = target[i];
426
+ const startSeg = start[i];
427
+ const endSeg = end[i];
428
+ if (targetSeg[0] === "Z") {
429
+ i++;
430
+ continue;
431
+ } else if (targetSeg[0] === "C") {
432
+ targetSeg[1] = startSeg[1] + (endSeg[1] - startSeg[1]) * t;
433
+ targetSeg[2] = startSeg[2] + (endSeg[2] - startSeg[2]) * t;
434
+ targetSeg[3] = startSeg[3] + (endSeg[3] - startSeg[3]) * t;
435
+ targetSeg[4] = startSeg[4] + (endSeg[4] - startSeg[4]) * t;
436
+ targetSeg[5] = startSeg[5] + (endSeg[5] - startSeg[5]) * t;
437
+ targetSeg[6] = startSeg[6] + (endSeg[6] - startSeg[6]) * t;
438
+ } else {
439
+ targetSeg[1] = startSeg[1] + (endSeg[1] - startSeg[1]) * t;
440
+ targetSeg[2] = startSeg[2] + (endSeg[2] - startSeg[2]) * t;
441
+ }
442
+ i++;
443
+ }
444
+ return target;
445
+ };
446
+ const supportedPathCommands = [
447
+ "M",
448
+ "L",
449
+ "C",
450
+ "Z"
451
+ ];
452
+ /**
453
+ * Check if an array of arrays is potentially a PathArray
454
+ * @param target The incoming value `constructor()` `from()` / `to()`
455
+ * @returns `true` when array is potentially a PathArray
456
+ */
457
+ const isPathLike = (value) => isArray(value) && value.some((seg) => isArray(seg) && supportedPathCommands.includes(seg[0]));
458
+ /**
459
+ * Check if an array of arrays is a valid PathArray for interpolation
460
+ * @param target The incoming value `from()` / `to()`
461
+ * @returns `true` when array is valid
462
+ */
463
+ const isValidPath = (value) => isPathLike(value) && value.length > 1 && value.every(isArray) && value.every(([cmd, ...values]) => supportedPathCommands.includes(cmd) && (["M", "L"].includes(cmd) && values.length === 2 && values.every(isNumber) || "C" === cmd && values.length === 6 && values.every(isNumber) || "Z" === cmd && values.length === 0));
464
+ /**
465
+ * Validate a PathArray and check if it's compatible with a reference.
466
+ *
467
+ * **NOTE**: Path interpolation only works when both paths have:
468
+ * - Identical segments structure (same number and order of M/L/C/Z)
469
+ * - Corresponding coordinates to interpolate
470
+ * Complex morphs require preprocessing (e.g. KUTE.js, Flubber)
471
+ *
472
+ * @example
473
+ * // simple shapes
474
+ * const linePath1 = [["M", 0, 0],["L", 50, 50]]
475
+ * const linePath2 = [["M",50,50],["L",150,150]]
476
+ * const curvePath1 = [["M", 0, 0],["C",15,15, 35, 35, 50, 50]]
477
+ * const curvePath2 = [["M",50,50],["C",50,50,100,100,150,150]]
478
+ *
479
+ * // closed shapes
480
+ * const closedLinePath1 = [["M", 0, 0],["L", 50, 50],["Z"]]
481
+ * const closedLinePath2 = [["M",50,50],["L",150,150],["Z"]]
482
+ * const closedCurvePath1 = [["M", 0, 0],["C",15,15, 35, 35, 50, 50],["Z"]]
483
+ * const closedCurvePath2 = [["M",50,50],["C",50,50,100,100,150,150],["Z"]]
484
+ *
485
+ * // composit shapes (multi-path)
486
+ * const compositPath1 = [
487
+ * ["M", 0, 0],["L",50,50],
488
+ * ["M",50,50],["C",50,50,100,100,150,150],
489
+ * ]
490
+ * const compositPath2 = [
491
+ * ["M",50,50],["L",150,150],
492
+ * ["M", 0, 0],["C", 15, 15,35,35,50,50],
493
+ * ]
494
+ *
495
+ * @param target The incoming value `from()` / `to()`
496
+ * @param ref The state reference value
497
+ * @returns `true` when arrays are compatible or a reason why not
498
+ */
499
+ const validatePath = (propName, target, ref) => {
500
+ if (!isValidPath(target)) return [false, `Property "${propName}" is not a valid PathArray.`];
501
+ if (ref) {
502
+ if (ref.length !== target.length) return [false, `Property "${propName}" is expecting an array of ${ref.length} path segments, got ${target.length}.`];
503
+ let i = 0;
504
+ const len = ref.length;
505
+ while (i < len) {
506
+ const refSeg = ref[i];
507
+ const targetSeg = target[i];
508
+ const refCmd = refSeg[0];
509
+ const targetCmd = targetSeg[0];
510
+ const refLen = refSeg.length;
511
+ const targetLen = targetSeg.length;
512
+ if (refCmd !== targetCmd || refLen !== targetLen) return [false, `Property "${propName}" mismatch at index ${i}. Segments don't match:\n> segment: "[${targetCmd}, ${targetSeg.slice(1)}]"\n> reference: "[${refCmd}, ${refSeg.slice(1)}]"`];
513
+ i++;
514
+ }
515
+ }
516
+ return [true];
517
+ };
518
+ /**
519
+ * Config for .use(propName, pathArrayConfig)
520
+ */
521
+ const pathArrayConfig = {
522
+ interpolate: interpolatePath,
523
+ validate: validatePath
524
+ };
525
+
526
+ //#endregion
527
+ //#region src/extend/object.ts
528
+ /**
529
+ * Single-level object interpolator
530
+ * **Note**: values must be validated first!
531
+ *
532
+ * Input: { scale: { x: 1, y: 1 } }
533
+ * Output: interpolated flat object with same structure
534
+ * @param target The target value of the object
535
+ * @param start The start value of the object
536
+ * @param end The end value of the object
537
+ * @param t The progress value
538
+ * @returns The interpolated flat object with same structure.
539
+ */
540
+ const interpolateObject = (target, start, end, t) => {
541
+ const keys = Object.keys(end);
542
+ let i = 0;
543
+ while (i < keys.length) {
544
+ const key = keys[i++];
545
+ const endVal = end[key];
546
+ const startVal = start[key];
547
+ target[key] = startVal + (endVal - startVal) * t;
548
+ }
549
+ return target;
550
+ };
551
+ /**
552
+ * Validate a simple plain object and compare its compatibility with a reference object.
553
+ * @param propName The property name to which this object belongs to
554
+ * @param target The target object itself
555
+ * @param ref A reference object to compare our target to
556
+ * @returns A [boolean, string?] tuple which represents [validity, "reason why not valid"]
557
+ */
558
+ const validateObject = (propName, target, ref) => {
559
+ if (!isPlainObject(target)) return [false, `Property "${propName}" must be a plain object.`];
560
+ const keys = Object.keys(target);
561
+ let i = 0;
562
+ const iLen = keys.length;
563
+ while (i < iLen) {
564
+ const key = keys[i++];
565
+ const value = target[key];
566
+ if (value === null || value === void 0) return [false, `Property "${key}" from "${propName}" is null/undefined.`];
567
+ if (!isNumber(value)) return [false, `Property "${key}" from "${propName}" must be a number.${isPlainObject(value) ? " Deeper nested objects are not supported." : ` Unsupported value: "${typeof value}".`}`];
568
+ if (ref) {
569
+ if (ref[key] === void 0) return [false, `Property "${key}" in "${propName}" doesn't exist in the reference object.`];
570
+ }
571
+ }
572
+ return [true];
573
+ };
574
+ /**
575
+ * Config for .use(propName, objectConfig)
576
+ */
577
+ const objectConfig = {
578
+ interpolate: interpolateObject,
579
+ validate: validateObject
580
+ };
581
+
582
+ //#endregion
583
+ //#region src/extend/transform.ts
584
+ /**
585
+ * Returns a valid CSS transform string either with transform functions (Eg.: `translate(15px) rotate(25deg)`)
586
+ * or `matrix(...)` / `matrix3d(...)`.
587
+ * When the `toMatrix` parameter is `true` it will create a DOMMatrix instance, apply transform
588
+ * steps and return a `matrix(...)` or `matrix3d(...)` string value.
589
+ * @param steps An array of TransformStep
590
+ * @param toMatrix An optional parameter to modify the function output
591
+ * @returns The valid CSS transform string value
592
+ */
593
+ const transformToString = (steps, toMatrix = false) => {
594
+ if (toMatrix) {
595
+ const matrix = new DOMMatrix();
596
+ const len = steps.length;
597
+ let i = 0;
598
+ while (i < len) {
599
+ const step = steps[i++];
600
+ switch (step[0]) {
601
+ case "perspective": {
602
+ const m2 = new DOMMatrix();
603
+ m2.m34 = -1 / step[1];
604
+ matrix.multiplySelf(m2);
605
+ break;
606
+ }
607
+ case "translate":
608
+ matrix.translateSelf(step[1], step[2] || 0, step[3] || 0);
609
+ break;
610
+ case "rotate":
611
+ matrix.rotateSelf(step[1], step[2] || 0, step[3] || 0);
612
+ break;
613
+ case "rotateAxisAngle":
614
+ matrix.rotateAxisAngleSelf(step[1], step[2], step[3], step[4]);
615
+ break;
616
+ case "scale":
617
+ matrix.scaleSelf(step[1], step[2] || 1, step[3] || 1);
618
+ break;
619
+ case "skewX":
620
+ matrix.skewXSelf(step[1]);
621
+ break;
622
+ case "skewY":
623
+ matrix.skewYSelf(step[1]);
624
+ break;
625
+ }
626
+ }
627
+ return matrix.toString();
628
+ }
629
+ const len = steps.length;
630
+ let i = 0;
631
+ let stringOutput = "";
632
+ while (i < len) {
633
+ const step = steps[i++];
634
+ switch (step[0]) {
635
+ case "perspective":
636
+ stringOutput += ` perspective(${step[1]}px)`;
637
+ break;
638
+ case "translate":
639
+ stringOutput += ` translate3d(${step[1]}px, ${step[2] || 0}px, ${step[3] || 0}px)`;
640
+ break;
641
+ case "rotate": {
642
+ const [rx, ry, rz] = step.slice(1);
643
+ if (typeof rx === "number" && ry === void 0 && rz === void 0) stringOutput += ` rotate(${step[1]}deg)`;
644
+ else {
645
+ stringOutput += ` rotateX(${step[1]}deg)`;
646
+ if (step[2] !== void 0) stringOutput += ` rotateY(${step[2]}deg)`;
647
+ if (step[3] !== void 0) stringOutput += ` rotateZ(${step[3]}deg)`;
648
+ }
649
+ break;
650
+ }
651
+ case "rotateAxisAngle":
652
+ stringOutput += ` rotate3d(${step[1]}, ${step[2]}, ${step[3]}, ${step[4]}deg)`;
653
+ break;
654
+ case "scale":
655
+ stringOutput += ` scale(${step[1]}, ${step[2] || step[1]}, ${step[3] || 1})`;
656
+ break;
657
+ case "skewX":
658
+ stringOutput += ` skewX(${step[1]}deg)`;
659
+ break;
660
+ case "skewY":
661
+ stringOutput += ` skewY(${step[1]}deg)`;
662
+ break;
663
+ }
664
+ }
665
+ return stringOutput.slice(1);
666
+ };
667
+ /**
668
+ * Convert euler rotation to axis angle.
669
+ * All values are degrees.
670
+ * @param x rotateX value
671
+ * @param y rotateY value
672
+ * @param z rotateZ value
673
+ * @returns The axis angle tuple [vectorX, vectorY, vectorZ, angle]
674
+ */
675
+ const eulerToAxisAngle = (x, y, z) => {
676
+ return quaternionToAxisAngle(eulerToQuaternion(x, y, z));
677
+ };
678
+ /**
679
+ * Convert euler rotation tuple to quaternion.
680
+ * All values are degrees.
681
+ * @param x The rotateX value
682
+ * @param y The rotateY value
683
+ * @param z The rotateZ value
684
+ * @returns The rotation quaternion
685
+ */
686
+ const eulerToQuaternion = (x, y, z) => {
687
+ const cx = Math.cos(x / 2);
688
+ const sx = Math.sin(x / 2);
689
+ const cy = Math.cos(y / 2);
690
+ const sy = Math.sin(y / 2);
691
+ const cz = Math.cos(z / 2);
692
+ const sz = Math.sin(z / 2);
693
+ return [
694
+ cx * cy * cz + sx * sy * sz,
695
+ sx * cy * cz - cx * sy * sz,
696
+ cx * sy * cz + sx * cy * sz,
697
+ cx * cy * sz - sx * sy * cz
698
+ ];
699
+ };
700
+ /**
701
+ * Convert euler rotation tuple to axis angle.
702
+ * All values are degrees.
703
+ * @param q The rotation quaternion
704
+ * @returns The axis angle tuple [vectorX, vectorY, vectorZ, angle]
705
+ */
706
+ const quaternionToAxisAngle = (q) => {
707
+ const [w, x, y, z] = q;
708
+ const len = Math.sqrt(x * x + y * y + z * z);
709
+ if (len < 1e-4) return [
710
+ 0,
711
+ 0,
712
+ 1,
713
+ 0
714
+ ];
715
+ const angle = 2 * Math.acos(Math.max(-1, Math.min(1, w)));
716
+ return [
717
+ x / len,
718
+ y / len,
719
+ z / len,
720
+ angle
721
+ ];
722
+ };
723
+ /**
724
+ * Interpolator: takes start/end arrays of `TransformStep`s → returns interpolated `TransformStep`s.
725
+ * **Note** - Like `PathArray`, these values are required to have same length and structure.
726
+ * @example
727
+ * const a1: TransformArray = [
728
+ * ["translate", 0, 0], // [translateX, translateY]
729
+ * ["rotate", 0], // [rotateZ]
730
+ * ["rotate", 0, 0], // [rotateX, rotateY]
731
+ * ["rotateAxisAngle", 0, 0, 0, 0], // [originX, originY, originZ, angle]
732
+ * ["scale", 1], // [scale]
733
+ * ["scale", 1, 1], // [scaleX, scaleY]
734
+ * ["perspective", 800], // [length]
735
+ * ];
736
+ * const a2: TransformArray = [
737
+ * ["translate", 50, 50],
738
+ * ["rotate", 45],
739
+ * ["rotate", 45, 45],
740
+ * ["rotateAxisAngle", 1, 0, 0, 45],
741
+ * ["scale", 1.5],
742
+ * ["scale", 1.5, 1.2],
743
+ * ["perspective", 400],
744
+ * ];
745
+ *
746
+ * @param target The target `TransformArray`
747
+ * @param start The start `TransformArray`
748
+ * @param end The end `TransformArray`
749
+ * @param t The progress value
750
+ * @returns The interpolated `TransformArray`
751
+ */
752
+ const interpolateTransform = (target, start, end, t) => {
753
+ const len = end.length;
754
+ let i = 0;
755
+ while (i < len) {
756
+ const targetStep = target[i];
757
+ const startStep = start[i];
758
+ const endStep = end[i];
759
+ switch (targetStep[0]) {
760
+ case "translate":
761
+ case "rotate":
762
+ case "scale":
763
+ case "rotateAxisAngle":
764
+ targetStep[1] = startStep[1] + (endStep[1] - startStep[1]) * t;
765
+ typeof endStep[2] === "number" && (targetStep[2] = startStep[2] + (endStep[2] - startStep[2]) * t);
766
+ typeof endStep[3] === "number" && (targetStep[3] = startStep[3] + (endStep[3] - startStep[3]) * t);
767
+ typeof endStep[4] === "number" && (targetStep[4] = startStep[4] + (endStep[4] - startStep[4]) * t);
768
+ break;
769
+ case "skewX":
770
+ case "skewY":
771
+ case "perspective":
772
+ targetStep[1] = startStep[1] + (endStep[1] - startStep[1]) * t;
773
+ break;
774
+ }
775
+ i++;
776
+ }
777
+ return target;
778
+ };
779
+ const supportedTransform = [
780
+ "perspective",
781
+ "translate",
782
+ "rotate",
783
+ "rotateAxisAngle",
784
+ "scale",
785
+ "skewX",
786
+ "skewY"
787
+ ];
788
+ /**
789
+ * Check if an array of arrays is potentially a TransformArray
790
+ * @param target The incoming value `constructor()` `from()` / `to()`
791
+ * @returns `true` when array is potentially a PathArray
792
+ */
793
+ const isTransformLike = (value) => isArray(value) && value.some((step) => isArray(step) && supportedTransform.includes(step[0]));
794
+ /**
795
+ * Check if an array of arrays is a valid TransformArray for interpolation
796
+ * @param target The incoming value `from()` / `to()`
797
+ * @returns `true` when array is valid
798
+ */
799
+ const isValidTransformArray = (value) => isTransformLike(value) && value.every(([fn, ...values]) => supportedTransform.includes(fn) && ([
800
+ "translate",
801
+ "rotate",
802
+ "scale"
803
+ ].includes(fn) && values.length > 0 && values.length <= 3 && values.every(isNumber) || "rotateAxisAngle" === fn && values.length === 4 && values.every(isNumber) || [
804
+ "skewX",
805
+ "skewY",
806
+ "perspective"
807
+ ].includes(fn) && values.length === 1 && isNumber(values[0])));
808
+ /**
809
+ * Validator for TransformArray
810
+ * Checks structure + number types + optional param counts
811
+ */
812
+ const validateTransform = (propName, target, ref) => {
813
+ if (!isValidTransformArray(target)) return [false, `Property "${propName}" must be an array of TransformStep.`];
814
+ if (ref) {
815
+ if (ref.length !== target.length) return [false, `Property "${propName}" is expecting an array of ${ref.length} transform steps, got ${target.length}.`];
816
+ let i = 0;
817
+ const len = target.length;
818
+ while (i < len) {
819
+ const step = target[i];
820
+ const refStep = ref[i];
821
+ const fn = step[0];
822
+ const fnRef = refStep[0];
823
+ if (refStep) {
824
+ if (fnRef !== fn || refStep.length !== step.length) return [false, `Property "${propName}" mismatch at index ${i}":\n> step: ["${fn}", ${step.slice(1)}]\n> reference: ["${fnRef}", ${refStep.slice(1)}]`];
825
+ }
826
+ i++;
827
+ }
828
+ }
829
+ return [true];
830
+ };
831
+ /**
832
+ * Config for .use("transform", transformConfig)
833
+ */
834
+ const transformConfig = {
835
+ interpolate: interpolateTransform,
836
+ validate: validateTransform
837
+ };
838
+
839
+ //#endregion
840
+ //#region src/Now.ts
841
+ let _nowFunc = () => globalThis.performance.now();
842
+ const now = () => {
843
+ return _nowFunc();
844
+ };
845
+ function setNow(nowFunction) {
846
+ _nowFunc = nowFunction;
847
+ }
848
+
849
+ //#endregion
850
+ //#region src/Runtime.ts
851
+ /**
852
+ * The runtime queue
853
+ */
854
+ const Queue = new Array(1e4).fill(null);
855
+ let rafID = 0;
856
+ let queueLength = 0;
857
+ /**
858
+ * The hot update loop updates all items in the queue,
859
+ * and stops automatically when there are no items left.
860
+ * @param t execution time (performance.now)
861
+ */
862
+ function Runtime(t = now()) {
863
+ let i = 0;
864
+ let writeIdx = 0;
865
+ while (i < queueLength) {
866
+ const item = Queue[i++];
867
+ if (item && item.update(t)) Queue[writeIdx++] = item;
868
+ }
869
+ queueLength = writeIdx;
870
+ if (queueLength === 0) {
871
+ cancelAnimationFrame(rafID);
872
+ rafID = 0;
873
+ } else rafID = requestAnimationFrame(Runtime);
874
+ }
875
+ /**
876
+ * Add a new item to the update loop.
877
+ * If it's the first item, it will also start the update loop.
878
+ * @param newItem Tween / Timeline
879
+ */
880
+ function addToQueue(newItem) {
881
+ const item = newItem;
882
+ Queue[queueLength++] = item;
883
+ if (!rafID) Runtime();
884
+ }
885
+ /**
886
+ * Remove item from the update loop.
887
+ * @param newItem Tween / Timeline
888
+ */
889
+ function removeFromQueue(removedItem) {
890
+ const idx = Queue.indexOf(removedItem);
891
+ if (idx !== -1) Queue[idx] = null;
892
+ }
893
+
894
+ //#endregion
895
+ //#region src/Tween.ts
896
+ /**
897
+ * Lightweight tween engine for interpolating values over time.
898
+ * Supports numbers and via extensions it enxtends to arrays
899
+ * (e.g. RGB, points), nested objects, and SVG path morphing.
900
+ *
901
+ * @template T - The type of the target object (usually a plain object with numeric properties)
902
+ *
903
+ * @example
904
+ * ```ts
905
+ * const tween = new Tween({ x: 0, opacity: 1 })
906
+ * .to({ x: 300, opacity: 0 })
907
+ * .duration(1.5)
908
+ * .easing(Easing.Elastic.Out)
909
+ * .start();
910
+ * ```
911
+ *
912
+ * @param initialValues The initial values object
913
+ */
914
+ var Tween = class {
915
+ state;
916
+ _state;
917
+ _startIsSet = false;
918
+ _repeat = 0;
919
+ _yoyo = false;
920
+ _reversed = false;
921
+ _initialRepeat = 0;
922
+ _startFired = false;
923
+ _propsStart = {};
924
+ _propsEnd = {};
925
+ _isPlaying = false;
926
+ _duration = 1e3;
927
+ _delay = 0;
928
+ _pauseStart = 0;
929
+ _repeatDelay = 0;
930
+ _startTime = 0;
931
+ _errors = /* @__PURE__ */ new Map();
932
+ _interpolators = /* @__PURE__ */ new Map();
933
+ _validators = /* @__PURE__ */ new Map();
934
+ _easing = (t) => t;
935
+ _onUpdate;
936
+ _onComplete;
937
+ _onStart;
938
+ _onStop;
939
+ _onPause;
940
+ _onResume;
941
+ _onRepeat;
942
+ _runtime = [];
943
+ /**
944
+ * Creates a new Tween instance.
945
+ * @param initialValues - The initial state of the animated object
946
+ */
947
+ constructor(initialValues) {
948
+ this.state = {};
949
+ validateValues.call(this, initialValues);
950
+ if (this._errors.size) this._state = initialValues;
951
+ else {
952
+ this.state = initialValues;
953
+ this._state = deproxy(initialValues);
954
+ }
955
+ return this;
956
+ }
957
+ /**
958
+ * A boolean that returns `true` when tween is playing.
959
+ */
960
+ get isPlaying() {
961
+ return this._isPlaying;
962
+ }
963
+ /**
964
+ * A boolean that returns `true` when tween is paused.
965
+ */
966
+ get isPaused() {
967
+ return this._pauseStart > 0;
968
+ }
969
+ /**
970
+ * A boolean that returns `true` when initial values are valid.
971
+ */
972
+ get isValidState() {
973
+ return Object.keys(this.state).length > 0;
974
+ }
975
+ /**
976
+ * A boolean that returns `true` when all values are valid.
977
+ */
978
+ get isValid() {
979
+ return this._errors.size === 0;
980
+ }
981
+ /**
982
+ * Returns the configured duration in seconds.
983
+ */
984
+ getDuration() {
985
+ return this._duration / 1e3;
986
+ }
987
+ /**
988
+ * Returns the total duration in seconds. It's calculated as a sum of
989
+ * the delay, duration multiplied by repeat value, repeat delay multiplied
990
+ * by repeat value.
991
+ */
992
+ get totalDuration() {
993
+ const repeat = this._initialRepeat;
994
+ return (this._delay + this._duration * (repeat + 1) + this._repeatDelay * repeat) / 1e3;
995
+ }
996
+ /**
997
+ * Returns the validator configured for a given property.
998
+ */
999
+ getValidator(propName) {
1000
+ return this._validators.get(propName);
1001
+ }
1002
+ /**
1003
+ * Returns the errors Map, mainly used by external validators.
1004
+ */
1005
+ getErrors() {
1006
+ return this._errors;
1007
+ }
1008
+ /**
1009
+ * Starts the tween (adds it to the global update loop).
1010
+ * Triggers `onStart` if set.
1011
+ * @param time - Optional explicit start time (defaults to `now()`)
1012
+ * @param overrideStart - If true, resets starting values even if already set
1013
+ * @returns this
1014
+ */
1015
+ start(time = now(), overrideStart = false) {
1016
+ if (this._isPlaying) return this;
1017
+ if (this._pauseStart) return this.resume();
1018
+ if (!this.isValid) {
1019
+ this._report();
1020
+ return this;
1021
+ }
1022
+ if (this._startTime && !overrideStart) this._resetState();
1023
+ if (!this._startIsSet || overrideStart) {
1024
+ this._startIsSet = true;
1025
+ this._setProps(this.state, this._propsStart, this._propsEnd, overrideStart);
1026
+ }
1027
+ this._isPlaying = true;
1028
+ this._startTime = time;
1029
+ this._startTime += this._delay;
1030
+ addToQueue(this);
1031
+ return this;
1032
+ }
1033
+ /**
1034
+ * Starts the tween from current values.
1035
+ * @param time - Optional explicit start time (defaults to `now()`)
1036
+ * @returns this
1037
+ */
1038
+ startFromLast(time = now()) {
1039
+ return this.start(time, true);
1040
+ }
1041
+ /**
1042
+ * Immediately stops the tween and removes it from the update loop.
1043
+ * Triggers `onStop` if set.
1044
+ * @returns this
1045
+ */
1046
+ stop() {
1047
+ if (!this._isPlaying) return this;
1048
+ removeFromQueue(this);
1049
+ this._isPlaying = false;
1050
+ this._repeat = this._initialRepeat;
1051
+ this._reversed = false;
1052
+ this._onStop?.(this.state);
1053
+ return this;
1054
+ }
1055
+ /**
1056
+ * Reverses playback direction and mirrors current time position.
1057
+ * @returns this
1058
+ */
1059
+ reverse() {
1060
+ if (!this._isPlaying) return this;
1061
+ const currentTime = now();
1062
+ const elapsed = currentTime - this._startTime;
1063
+ this._startTime = currentTime - (this._duration - elapsed);
1064
+ this._reversed = !this._reversed;
1065
+ if (this._initialRepeat > 0) this._repeat = this._initialRepeat - this._repeat;
1066
+ return this;
1067
+ }
1068
+ /**
1069
+ * Pause playback and capture the pause time.
1070
+ * @param time - Time of pause
1071
+ * @returns this
1072
+ */
1073
+ pause(time = now()) {
1074
+ if (!this._isPlaying) return this;
1075
+ this._pauseStart = time;
1076
+ this._isPlaying = false;
1077
+ this._onPause?.(this.state);
1078
+ return this;
1079
+ }
1080
+ /**
1081
+ * Resume playback and reset the pause time.
1082
+ * @param time - Time of pause
1083
+ * @returns this
1084
+ */
1085
+ resume(time = now()) {
1086
+ if (!this._pauseStart) return this;
1087
+ this._startTime += time - this._pauseStart;
1088
+ this._pauseStart = 0;
1089
+ this._isPlaying = true;
1090
+ this._onResume?.(this.state);
1091
+ addToQueue(this);
1092
+ return this;
1093
+ }
1094
+ /**
1095
+ * Sets the starting values for properties.
1096
+ * @param startValues - Partial object with starting values
1097
+ * @returns this
1098
+ */
1099
+ from(startValues) {
1100
+ if (!this.isValidState || this.isPlaying) return this;
1101
+ this._evaluate(startValues);
1102
+ if (this.isValid) {
1103
+ Object.assign(this._propsStart, startValues);
1104
+ this._startIsSet = false;
1105
+ }
1106
+ return this;
1107
+ }
1108
+ /**
1109
+ * Sets the ending values for properties.
1110
+ * @param endValues - Partial object with target values
1111
+ * @returns this
1112
+ */
1113
+ to(endValues) {
1114
+ if (!this.isValidState || this.isPlaying) return this;
1115
+ this._evaluate(endValues);
1116
+ if (this.isValid) {
1117
+ this._propsEnd = endValues;
1118
+ this._startIsSet = false;
1119
+ }
1120
+ return this;
1121
+ }
1122
+ /**
1123
+ * Sets the duration of the tween in seconds.
1124
+ * Internally it's converted to milliseconds.
1125
+ * @param duration - Time in seconds
1126
+ * @default 1 second
1127
+ * @returns this
1128
+ */
1129
+ duration(seconds = 1) {
1130
+ this._duration = seconds * 1e3;
1131
+ return this;
1132
+ }
1133
+ /**
1134
+ * Sets the delay in seconds before the tween starts.
1135
+ * Internally it's converted to milliseconds.
1136
+ * @param delay - Time in seconds
1137
+ * @default 0 seconds
1138
+ * @returns this
1139
+ */
1140
+ delay(seconds = 0) {
1141
+ this._delay = seconds * 1e3;
1142
+ return this;
1143
+ }
1144
+ /**
1145
+ * Sets how many times to repeat.
1146
+ * @param times - How many times to repeat
1147
+ * @default 0 times
1148
+ * @returns this
1149
+ */
1150
+ repeat(times = 0) {
1151
+ this._repeat = times;
1152
+ this._initialRepeat = times;
1153
+ return this;
1154
+ }
1155
+ /**
1156
+ * Sets a number of seconds to delay the animation
1157
+ * after each repeat.
1158
+ * @param amount - How many seconds to delay
1159
+ * @default 0 seconds
1160
+ * @returns this
1161
+ */
1162
+ repeatDelay(amount = 0) {
1163
+ this._repeatDelay = amount * 1e3;
1164
+ return this;
1165
+ }
1166
+ /**
1167
+ * Sets to tween from end to start values.
1168
+ * The easing is also goes backwards.
1169
+ * This requires repeat value of at least 1.
1170
+ * @param yoyo - When `true` values are reversed on every uneven repeat
1171
+ * @default false
1172
+ * @returns this
1173
+ */
1174
+ yoyo(yoyo = false) {
1175
+ this._yoyo = yoyo;
1176
+ return this;
1177
+ }
1178
+ /**
1179
+ * Sets the easing function.
1180
+ * @param easing - Function that maps progress [0,1] → eased progress [0,1]
1181
+ * @default linear
1182
+ * @returns this
1183
+ */
1184
+ easing(easing = (t) => t) {
1185
+ this._easing = easing;
1186
+ return this;
1187
+ }
1188
+ /**
1189
+ * Registers a callback fired when `.start()` is called.
1190
+ * @param callback - Receives state at start time
1191
+ * @returns this
1192
+ */
1193
+ onStart(callback) {
1194
+ this._onStart = callback;
1195
+ return this;
1196
+ }
1197
+ /**
1198
+ * Registers a callback fired on every frame.
1199
+ * @param callback - Receives current state, elapsed (0–1)
1200
+ * @returns this
1201
+ */
1202
+ onUpdate(callback) {
1203
+ this._onUpdate = callback;
1204
+ return this;
1205
+ }
1206
+ /**
1207
+ * Registers a callback fired when the tween reaches progress = 1.
1208
+ * @param callback - Receives final state
1209
+ * @returns this
1210
+ */
1211
+ onComplete(callback) {
1212
+ this._onComplete = callback;
1213
+ return this;
1214
+ }
1215
+ /**
1216
+ * Registers a callback fired when `.stop()` is called.
1217
+ * @param callback - Receives state at stop time
1218
+ * @returns this
1219
+ */
1220
+ onStop(callback) {
1221
+ this._onStop = callback;
1222
+ return this;
1223
+ }
1224
+ /**
1225
+ * Registers a callback fired when `pause()` was called.
1226
+ */
1227
+ onPause(cb) {
1228
+ this._onPause = cb;
1229
+ return this;
1230
+ }
1231
+ /**
1232
+ * Registers a callback fired when `.resume()` was called.
1233
+ */
1234
+ onResume(cb) {
1235
+ this._onResume = cb;
1236
+ return this;
1237
+ }
1238
+ /**
1239
+ * Registers a callback that is invoked **every time** one full cycle
1240
+ * (repeat iteration) * of the tween has completed — but **before**
1241
+ * the next repeat begins (if any remain).
1242
+ *
1243
+ * This is different from `onComplete`, which only fires once at the
1244
+ * very end of the entire tween (after all repeats are finished).
1245
+ */
1246
+ onRepeat(cb) {
1247
+ this._onRepeat = cb;
1248
+ return this;
1249
+ }
1250
+ /**
1251
+ * Manually advances the tween to the given time.
1252
+ * @param time - Current absolute time (performance.now style)
1253
+ *
1254
+ * @returns `true` if the tween is still playing after the update, `false`
1255
+ * otherwise.
1256
+ */
1257
+ update(time = now()) {
1258
+ if (!this._isPlaying) return false;
1259
+ if (time < this._startTime) return true;
1260
+ if (!this._startFired) {
1261
+ this._onStart?.(this.state);
1262
+ this._startFired = true;
1263
+ }
1264
+ const reversed = this._reversed;
1265
+ const state = this.state;
1266
+ const runtime = this._runtime;
1267
+ let progress = (time - this._startTime) / this._duration;
1268
+ if (progress > 1) progress = 1;
1269
+ let eased = this._easing(reversed ? 1 - progress : progress);
1270
+ eased = reversed ? 1 - eased : eased;
1271
+ const len = runtime.length;
1272
+ let i = 0;
1273
+ while (i < len) {
1274
+ const prop = runtime[i++];
1275
+ const targetObject = prop[0];
1276
+ const property = prop[1];
1277
+ const interpolator = prop[2];
1278
+ const startVal = reversed ? prop[4] : prop[3];
1279
+ const endVal = reversed ? prop[3] : prop[4];
1280
+ if (typeof endVal === "number") state[property] = startVal + (endVal - startVal) * eased;
1281
+ else interpolator(targetObject, startVal, endVal, eased);
1282
+ }
1283
+ this._onUpdate?.(state, progress);
1284
+ if (progress === 1) {
1285
+ if (this._repeat === 0) {
1286
+ this._isPlaying = false;
1287
+ this._repeat = this._initialRepeat;
1288
+ this._reversed = false;
1289
+ this._onComplete?.(state);
1290
+ return false;
1291
+ }
1292
+ if (this._repeat !== Infinity) this._repeat--;
1293
+ if (this._yoyo) this._reversed = !reversed;
1294
+ this._startTime = time;
1295
+ this._startTime += this._repeatDelay;
1296
+ this._onRepeat?.(state);
1297
+ return true;
1298
+ }
1299
+ return true;
1300
+ }
1301
+ /**
1302
+ * Public method to register an extension for a given property.
1303
+ *
1304
+ * **NOTES**
1305
+ * - the extension will validate the initial values once `.use()` is called.
1306
+ * - the `.use()` method must be called before `.to()` / `.from()`.
1307
+ *
1308
+ * @param property The property name
1309
+ * @param extension The extension object
1310
+ * @returns this
1311
+ *
1312
+ * @example
1313
+ *
1314
+ * const tween = new Tween({ myProp: { x: 0, y: 0 } });
1315
+ * tween.use("myProp", objectConfig);
1316
+ */
1317
+ use(property, { interpolate, validate }) {
1318
+ if (interpolate && !this._interpolators.has(property)) this._interpolators.set(property, interpolate);
1319
+ if (validate && !this._validators.has(property)) this._validators.set(property, validate);
1320
+ this._evaluate();
1321
+ return this;
1322
+ }
1323
+ /**
1324
+ * Internal method to reset state to initial values.
1325
+ * @internal
1326
+ */
1327
+ _resetState() {
1328
+ deepAssign(this.state, this._state);
1329
+ }
1330
+ /**
1331
+ * Reset starting values, end values and runtime.
1332
+ */
1333
+ clear() {
1334
+ this._propsStart = {};
1335
+ this._propsEnd = {};
1336
+ this._runtime.length = 0;
1337
+ this._startTime = 0;
1338
+ this._pauseStart = 0;
1339
+ this._repeat = 0;
1340
+ this._initialRepeat = 0;
1341
+ return this;
1342
+ }
1343
+ /**
1344
+ * Internal method to handle instrumentation of start and end values for interpolation.
1345
+ * @internal
1346
+ */
1347
+ _setProps(obj, propsStart, propsEnd, overrideStartingValues) {
1348
+ const endKeys = Object.keys(propsEnd);
1349
+ const len = endKeys.length;
1350
+ this._runtime.length = 0;
1351
+ let rtLen = 0;
1352
+ let i = 0;
1353
+ while (i < len) {
1354
+ const property = endKeys[i++];
1355
+ if (typeof propsStart[property] === "undefined" || overrideStartingValues) {
1356
+ const objValue = obj[property];
1357
+ if (isObject(objValue) || isArray(objValue)) propsStart[property] = deproxy(objValue);
1358
+ else propsStart[property] = objValue;
1359
+ const interpolator = this._interpolators.get(property) || null;
1360
+ this._runtime[rtLen++] = [
1361
+ objValue,
1362
+ property,
1363
+ interpolator,
1364
+ propsStart[property],
1365
+ propsEnd[property]
1366
+ ];
1367
+ }
1368
+ }
1369
+ }
1370
+ /**
1371
+ * Internal method to handle validation of initial values, start and end values.
1372
+ * @internal
1373
+ */
1374
+ _evaluate(newObj) {
1375
+ if (!this.isValidState) {
1376
+ const temp = this._state;
1377
+ validateValues.call(this, temp);
1378
+ if (this.isValid) {
1379
+ this.state = temp;
1380
+ this._state = deproxy(temp);
1381
+ }
1382
+ } else if (newObj) validateValues.call(this, newObj, this._state);
1383
+ return this;
1384
+ }
1385
+ /**
1386
+ * Internal method to provide feedback on validation issues.
1387
+ * @internal
1388
+ */
1389
+ _report() {
1390
+ if (!this.isValid) {
1391
+ const message = ["[Tween] failed validation:", "- " + Array.from(this._errors.values()).join("\n- ")];
1392
+ console.warn(message.join("\n"));
1393
+ }
1394
+ return this;
1395
+ }
1396
+ };
1397
+
1398
+ //#endregion
1399
+ //#region src/Timeline.ts
1400
+ /**
1401
+ * Timeline orchestrates multiple tweens with scheduling, overlaps, labels and repeat.
1402
+ * Supports numbers and via extensions it enxtends to arrays
1403
+ * (e.g. RGB, points), nested objects, and SVG path morphing.
1404
+ *
1405
+ * @template T - Type of the animated state object
1406
+ *
1407
+ * @example
1408
+ * ```ts
1409
+ * const tl = new Timeline({ x: 0, opacity: 0 })
1410
+ * .to({ x: 300, duration: 1.2 })
1411
+ * .to({ opacity: 1, duration: 0.8 }, "-=0.4")
1412
+ * .play();
1413
+ * ```
1414
+ *
1415
+ * @param initialValues The initial values object
1416
+ */
1417
+ var Timeline = class {
1418
+ state;
1419
+ _state;
1420
+ _entries = [];
1421
+ _labels = /* @__PURE__ */ new Map();
1422
+ _progress = 0;
1423
+ _duration = 0;
1424
+ _yoyo = false;
1425
+ _reversed = false;
1426
+ _time = 0;
1427
+ _pauseTime = 0;
1428
+ _lastTime = 0;
1429
+ _isPlaying = false;
1430
+ _repeat = 0;
1431
+ _repeatDelay = 0;
1432
+ _repeatDelayStart = 0;
1433
+ _initialRepeat = 0;
1434
+ _errors = /* @__PURE__ */ new Map();
1435
+ _interpolators = /* @__PURE__ */ new Map();
1436
+ _validators = /* @__PURE__ */ new Map();
1437
+ _onStart;
1438
+ _onStop;
1439
+ _onPause;
1440
+ _onResume;
1441
+ _onUpdate;
1442
+ _onComplete;
1443
+ _onRepeat;
1444
+ /**
1445
+ * Creates a new Timeline instance.
1446
+ * @param initialValues - The initial state of the animated object
1447
+ */
1448
+ constructor(initialValues) {
1449
+ this.state = {};
1450
+ validateValues.call(this, initialValues);
1451
+ if (this._errors.size) this._state = initialValues;
1452
+ else {
1453
+ this.state = initialValues;
1454
+ this._state = { ...initialValues };
1455
+ }
1456
+ return this;
1457
+ }
1458
+ /**
1459
+ * Returns the current [0-1] progress value.
1460
+ */
1461
+ get progress() {
1462
+ return this._progress;
1463
+ }
1464
+ /**
1465
+ * Returns the total duration in seconds.
1466
+ */
1467
+ get duration() {
1468
+ return this._duration / 1e3;
1469
+ }
1470
+ /**
1471
+ * Returns the total duration in seconds, which is a sum of all entries duration
1472
+ * multiplied by repeat value and repeat delay multiplied by repeat value.
1473
+ */
1474
+ get totalDuration() {
1475
+ const repeat = this._initialRepeat;
1476
+ return (this._duration * (repeat + 1) + this._repeatDelay * repeat) / 1e3;
1477
+ }
1478
+ /**
1479
+ * A boolean that returns `true` when timeline is playing.
1480
+ */
1481
+ get isPlaying() {
1482
+ return this._isPlaying;
1483
+ }
1484
+ /**
1485
+ * A boolean that returns `true` when timeline is paused.
1486
+ */
1487
+ get isPaused() {
1488
+ return !this._isPlaying && this._pauseTime > 0;
1489
+ }
1490
+ /**
1491
+ * A boolean that returns `true` when initial values are valid.
1492
+ */
1493
+ get isValidState() {
1494
+ return Object.keys(this.state).length > 0;
1495
+ }
1496
+ /**
1497
+ * A boolean that returns `true` when all values are valid.
1498
+ */
1499
+ get isValid() {
1500
+ return this._errors.size === 0;
1501
+ }
1502
+ /**
1503
+ * Returns the validator configured for a given property.
1504
+ */
1505
+ getValidator(propName) {
1506
+ return this._validators.get(propName);
1507
+ }
1508
+ /**
1509
+ * Returns the errors Map, mainly used by external validators.
1510
+ */
1511
+ getErrors() {
1512
+ return this._errors;
1513
+ }
1514
+ /**
1515
+ * Starts or resumes playback from the beginning (or current time if resumed).
1516
+ * Triggers the `onStart` callback if set.
1517
+ * @param startTime - Optional explicit start timestamp (defaults to now)
1518
+ * @returns this
1519
+ */
1520
+ play(time = now()) {
1521
+ if (this._pauseTime) return this.resume();
1522
+ if (this._isPlaying) return this;
1523
+ if (!this.isValid) {
1524
+ this._report();
1525
+ return this;
1526
+ }
1527
+ if (this._time) this._resetState();
1528
+ this._isPlaying = true;
1529
+ this._lastTime = time;
1530
+ this._time = 0;
1531
+ this._onStart?.(this.state, 0);
1532
+ addToQueue(this);
1533
+ return this;
1534
+ }
1535
+ /**
1536
+ * Pauses playback (preserves current time).
1537
+ * Triggers the `onPause` callback if set.
1538
+ * @returns this
1539
+ */
1540
+ pause(time = now()) {
1541
+ if (!this._isPlaying) return this;
1542
+ this._isPlaying = false;
1543
+ this._pauseTime = time;
1544
+ this._onPause?.(this.state, this.progress);
1545
+ return this;
1546
+ }
1547
+ /**
1548
+ * Resumes from paused state (adjusts internal clock).
1549
+ * Triggers the `onResume` callback if set.
1550
+
1551
+ * @param time - Optional current timestamp (defaults to now)
1552
+ * @returns this
1553
+ */
1554
+ resume(time = now()) {
1555
+ if (this._isPlaying) return this;
1556
+ this._isPlaying = true;
1557
+ const dif = time - this._pauseTime;
1558
+ this._pauseTime = 0;
1559
+ this._lastTime += dif;
1560
+ this._onResume?.(this.state, this.progress);
1561
+ addToQueue(this);
1562
+ return this;
1563
+ }
1564
+ /**
1565
+ * Reverses playback direction and mirrors current time position.
1566
+ * @returns this
1567
+ */
1568
+ reverse() {
1569
+ if (!this._isPlaying) return this;
1570
+ this._reversed = !this._reversed;
1571
+ this._time = this._duration - this._time;
1572
+ if (this._initialRepeat > 0) this._repeat = this._initialRepeat - this._repeat;
1573
+ return this;
1574
+ }
1575
+ /**
1576
+ * Jumps to a specific time or label. When playback is reversed
1577
+ * the time is adjusted.
1578
+ * @param pointer - Seconds or label name
1579
+ * @returns this
1580
+ */
1581
+ seek(pointer) {
1582
+ this._time = this._resolvePosition(pointer);
1583
+ return this;
1584
+ }
1585
+ /**
1586
+ * Stops playback, resets time to 0, and restores initial state.
1587
+ * Triggers the `onStop` callback if set.
1588
+ * @returns this
1589
+ */
1590
+ stop() {
1591
+ if (!this._isPlaying) return this;
1592
+ this._isPlaying = false;
1593
+ this._time = 0;
1594
+ this._pauseTime = 0;
1595
+ this._repeat = this._initialRepeat;
1596
+ this._reversed = false;
1597
+ removeFromQueue(this);
1598
+ this._onStop?.(this.state, this._progress);
1599
+ return this;
1600
+ }
1601
+ /**
1602
+ * Sets the number of times the timeline should repeat.
1603
+ * @param count - Number of repeats (0 = once, Infinity = loop forever)
1604
+ * @returns this
1605
+ */
1606
+ repeat(count = 0) {
1607
+ this._repeat = count;
1608
+ this._initialRepeat = count;
1609
+ return this;
1610
+ }
1611
+ /**
1612
+ * Sets a number of seconds to delay the animation
1613
+ * after each repeat.
1614
+ * @param amount - How many seconds to delay
1615
+ * @default 0 seconds
1616
+ * @returns this
1617
+ */
1618
+ repeatDelay(amount = 0) {
1619
+ this._repeatDelay = amount * 1e3;
1620
+ return this;
1621
+ }
1622
+ /**
1623
+ * Sets to Timeline entries to tween from end to start values.
1624
+ * The easing is also goes backwards.
1625
+ * This requires repeat value of at least 1.
1626
+ * @param yoyo - When `true` values are reversed
1627
+ * @default false
1628
+ * @returns this
1629
+ */
1630
+ yoyo(yoyo = false) {
1631
+ this._yoyo = yoyo;
1632
+ return this;
1633
+ }
1634
+ /**
1635
+ * Adds a named time position for use in `.seek("label")`.
1636
+ * @param name - Label identifier
1637
+ * @param position - Time offset or relative position
1638
+ * @returns this
1639
+ */
1640
+ label(name, position) {
1641
+ this._labels.set(name, this._resolvePosition(position));
1642
+ return this;
1643
+ }
1644
+ /**
1645
+ * Adds a new tween entry to the timeline.
1646
+ * @param config - Values to animate + duration, easing, etc.
1647
+ * @param position - Start offset: number, "+=0.5", "-=0.3", or label name
1648
+ * @returns this (chainable)
1649
+ */
1650
+ to({ duration = 1, easing = (t) => t, ...values }, position = "+=0") {
1651
+ if (!this.isValidState || this._isPlaying) return this;
1652
+ this._evaluate(values);
1653
+ if (this.isValid) {
1654
+ const startTime = this._resolvePosition(position);
1655
+ const to = values;
1656
+ const from = {};
1657
+ const entryDuration = duration * 1e3;
1658
+ this._entries.push({
1659
+ from,
1660
+ to,
1661
+ runtime: [],
1662
+ startTime,
1663
+ duration: entryDuration,
1664
+ easing,
1665
+ isActive: false
1666
+ });
1667
+ const endTime = startTime + entryDuration;
1668
+ this._duration = Math.max(this._duration, endTime);
1669
+ }
1670
+ return this;
1671
+ }
1672
+ /**
1673
+ * Registers a callback fired when playback begins.
1674
+ */
1675
+ onStart(cb) {
1676
+ this._onStart = cb;
1677
+ return this;
1678
+ }
1679
+ /**
1680
+ * Registers a callback fired when `pause()` was called.
1681
+ */
1682
+ onPause(cb) {
1683
+ this._onPause = cb;
1684
+ return this;
1685
+ }
1686
+ /**
1687
+ * Registers a callback fired when `.play()` / `.resume()` was called.
1688
+ */
1689
+ onResume(cb) {
1690
+ this._onResume = cb;
1691
+ return this;
1692
+ }
1693
+ /**
1694
+ * Registers a callback fired on explicit `.stop()`.
1695
+ */
1696
+ onStop(cb) {
1697
+ this._onStop = cb;
1698
+ return this;
1699
+ }
1700
+ /**
1701
+ * Registers a callback fired every frame.
1702
+ */
1703
+ onUpdate(cb) {
1704
+ this._onUpdate = cb;
1705
+ return this;
1706
+ }
1707
+ /**
1708
+ * Registers a callback fired when timeline naturally completes.
1709
+ */
1710
+ onComplete(cb) {
1711
+ this._onComplete = cb;
1712
+ return this;
1713
+ }
1714
+ /**
1715
+ * Registers a callback fired when `.play()` / `.resume()` was called.
1716
+ */
1717
+ onRepeat(cb) {
1718
+ this._onRepeat = cb;
1719
+ return this;
1720
+ }
1721
+ /**
1722
+ * Public method to register an extension for a given property.
1723
+ *
1724
+ * **NOTES**
1725
+ * - the extension will validate the initial values once `.use()` is called.
1726
+ * - the `.use()` method must be called before `.to()`.
1727
+ *
1728
+ * @param property The property name
1729
+ * @param extension The extension object
1730
+ * @returns this
1731
+ *
1732
+ * @example
1733
+ *
1734
+ * const timeline = new Timeline({ myProp: { x: 0, y: 0 } });
1735
+ * timeline.use("myProp", objectConfig);
1736
+ */
1737
+ use(property, { interpolate, validate }) {
1738
+ if (interpolate && !this._interpolators.has(property)) this._interpolators.set(property, interpolate);
1739
+ if (validate && !this._validators.has(property)) this._validators.set(property, validate);
1740
+ this._evaluate();
1741
+ return this;
1742
+ }
1743
+ /**
1744
+ * Manually advances the timeline to the given time.
1745
+ * @param time - Current absolute time (performance.now style)
1746
+ *
1747
+ * @returns `true` if the timeline is still playing after the update, `false`
1748
+ * otherwise.
1749
+ */
1750
+ update(time = now()) {
1751
+ if (!this._isPlaying) return false;
1752
+ if (this._repeatDelayStart) {
1753
+ if (time - this._repeatDelayStart < this._repeatDelay) {
1754
+ this._lastTime = time;
1755
+ return true;
1756
+ }
1757
+ this._repeatDelayStart = 0;
1758
+ }
1759
+ const delta = time - this._lastTime;
1760
+ const reversed = this._reversed;
1761
+ this._lastTime = time;
1762
+ this._time += delta;
1763
+ this._progress = this._time > this._duration ? 1 : this._time / this._duration;
1764
+ const entries = this._entries;
1765
+ const state = this.state;
1766
+ const entriesLen = entries.length;
1767
+ let i = 0;
1768
+ while (i < entriesLen) {
1769
+ const entry = entries[i++];
1770
+ const startTime = !reversed ? entry.startTime : this._duration - entry.startTime - entry.duration;
1771
+ let tweenElapsed = (this._time - startTime) / entry.duration;
1772
+ if (tweenElapsed > 1) tweenElapsed = 1;
1773
+ if (tweenElapsed < 0) tweenElapsed = 0;
1774
+ if (!entry.isActive && tweenElapsed > 0 && tweenElapsed < 1) {
1775
+ if (entry.runtime.length === 0) this._setEntry(entry, state);
1776
+ entry.isActive = true;
1777
+ }
1778
+ if (entry.isActive) {
1779
+ let easedValue = entry.easing(reversed ? 1 - tweenElapsed : tweenElapsed);
1780
+ easedValue = reversed ? 1 - easedValue : easedValue;
1781
+ const runtime = entry.runtime;
1782
+ const runtimeLen = runtime.length;
1783
+ let j = 0;
1784
+ while (j < runtimeLen) {
1785
+ const prop = runtime[j++];
1786
+ const targetObject = prop[0];
1787
+ const property = prop[1];
1788
+ const interpolator = prop[2];
1789
+ const startVal = reversed ? prop[4] : prop[3];
1790
+ const endVal = reversed ? prop[3] : prop[4];
1791
+ if (typeof endVal === "number") state[property] = startVal + (endVal - startVal) * easedValue;
1792
+ else interpolator(targetObject, startVal, endVal, easedValue);
1793
+ }
1794
+ if (tweenElapsed === 1) entry.isActive = false;
1795
+ }
1796
+ }
1797
+ this._onUpdate?.(state, this._progress);
1798
+ if (this._progress === 1) {
1799
+ if (this._repeat === 0) {
1800
+ this._isPlaying = false;
1801
+ this._repeat = this._initialRepeat;
1802
+ this._reversed = false;
1803
+ this._onComplete?.(state, 1);
1804
+ this._resetState(true);
1805
+ return false;
1806
+ }
1807
+ if (this._repeat !== Infinity) this._repeat--;
1808
+ if (this._yoyo) this._reversed = !reversed;
1809
+ this._time = 0;
1810
+ this._resetState();
1811
+ this._onRepeat?.(state, this.progress);
1812
+ if (this._repeatDelay > 0) this._repeatDelayStart = time;
1813
+ return true;
1814
+ }
1815
+ return true;
1816
+ }
1817
+ /**
1818
+ * Public method to clear all entries, labels and reset timers to zero
1819
+ * or initial value (repeat).
1820
+ */
1821
+ clear() {
1822
+ this._entries.length = 0;
1823
+ this._duration = 0;
1824
+ this._labels.clear();
1825
+ this._time = 0;
1826
+ this._progress = 0;
1827
+ this._pauseTime = 0;
1828
+ this._lastTime = 0;
1829
+ this._repeatDelay = 0;
1830
+ this._repeat = this._initialRepeat;
1831
+ this._repeatDelayStart = 0;
1832
+ this._reversed = false;
1833
+ return this;
1834
+ }
1835
+ /**
1836
+ * Internal method to handle instrumentation of start and end values for interpolation
1837
+ * of a tween entry. Only called once per entry on first activation.
1838
+ * @internal
1839
+ */
1840
+ _setEntry(entry, state) {
1841
+ const from = entry.from;
1842
+ const to = entry.to;
1843
+ const keysTo = Object.keys(to);
1844
+ const keyLen = keysTo.length;
1845
+ entry.runtime = new Array(keyLen);
1846
+ let rtLen = 0;
1847
+ let j = 0;
1848
+ while (j < keyLen) {
1849
+ const key = keysTo[j++];
1850
+ const objValue = state[key];
1851
+ if (isObject(objValue) || isArray(objValue)) from[key] = deproxy(objValue);
1852
+ else from[key] = objValue;
1853
+ const interpolator = this._interpolators.get(key) || null;
1854
+ entry.runtime[rtLen++] = [
1855
+ objValue,
1856
+ key,
1857
+ interpolator,
1858
+ from[key],
1859
+ to[key]
1860
+ ];
1861
+ }
1862
+ }
1863
+ /**
1864
+ * Internal method to revert state to initial values and reset entry flags.
1865
+ * @internal
1866
+ */
1867
+ _resetState(isComplete = false) {
1868
+ let i = 0;
1869
+ const entriesLen = this._entries.length;
1870
+ while (i < entriesLen) {
1871
+ const entry = this._entries[i++];
1872
+ entry.isActive = false;
1873
+ }
1874
+ if (!isComplete) deepAssign(this.state, this._state);
1875
+ }
1876
+ /**
1877
+ * Internal method to resolve the position relative to the current duration
1878
+ * or a set value in seconds.
1879
+ * @internal
1880
+ */
1881
+ _resolvePosition(pos) {
1882
+ if (typeof pos === "number") return Math.min(this._duration, Math.max(0, pos * 1e3));
1883
+ if (typeof pos === "string") {
1884
+ const labelTime = this._labels.get(pos);
1885
+ if (labelTime !== void 0) return labelTime;
1886
+ if (pos.startsWith("+=") || pos.startsWith("-=")) {
1887
+ let offset = parseFloat(pos.slice(2));
1888
+ if (isNaN(offset)) offset = 0;
1889
+ offset *= 1e3;
1890
+ return pos.startsWith("+=") ? this._duration + offset : Math.max(0, this._duration - offset);
1891
+ }
1892
+ }
1893
+ return this._duration;
1894
+ }
1895
+ /**
1896
+ * Internal method to handle validation of initial values and entries values.
1897
+ * @internal
1898
+ */
1899
+ _evaluate(newObj) {
1900
+ if (!this.isValidState) {
1901
+ const temp = this._state;
1902
+ validateValues.call(this, temp);
1903
+ if (this.isValid) {
1904
+ this.state = temp;
1905
+ this._state = deproxy(temp);
1906
+ }
1907
+ } else if (newObj) validateValues.call(this, newObj, this._state);
1908
+ return this;
1909
+ }
1910
+ /**
1911
+ * Internal method to provide feedback on validation issues.
1912
+ * @internal
1913
+ */
1914
+ _report() {
1915
+ if (!this.isValid) {
1916
+ const message = ["[Timeline] failed validation:", "- " + Array.from(this._errors.values()).join("\n- ")].join("\n");
1917
+ console.warn(message);
1918
+ }
1919
+ return this;
1920
+ }
1921
+ };
1922
+
1923
+ //#endregion
1924
+ //#region package.json
1925
+ var version = "0.0.3";
1926
+
1927
+ //#endregion
1928
+ export { Easing, Queue, Runtime, Timeline, Tween, addToQueue, arrayConfig, deepAssign, deproxy, dummyInstance, eulerToAxisAngle, interpolateArray, interpolateObject, interpolatePath, interpolateTransform, isArray, isDeepObject, isFunction, isNumber, isObject, isPathLike, isPlainObject, isServer, isString, isTransformLike, isValidArray, isValidPath, isValidTransformArray, now, objectConfig, objectHasProp, pathArrayConfig, pathToString, removeFromQueue, roundTo, setNow, transformConfig, transformToString, validateArray, validateObject, validatePath, validateTransform, validateValues, version };
1929
+ //# sourceMappingURL=index.mjs.map