coily 0.11.0 → 0.12.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.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as SpringPosition, c as defineSpring, i as Spring, n as createSpringSystem, o as SpringConfig, r as TickerOptions, s as SpringOptions, t as SpringSystem } from "./system-B_kZMcpg.js";
2
- export { type Spring, type SpringConfig, type SpringOptions, type SpringPosition, type SpringSystem, type TickerOptions, createSpringSystem, defineSpring };
1
+ import { _ as defineSpring, a as Spring2D, c as Spring2DWithOffset, d as SpringPosition, f as SpringTarget, g as SpringState, h as SpringOptions, i as TickerOptions, l as Vector2, m as SpringConfig, n as SpringSystemOptions, o as Spring2DPosition, p as SpringWithOffset, r as createSpringSystem, s as Spring2DTarget, t as SpringSystem, u as Spring } from "./system-CMaMt_7p.js";
2
+ export { type Spring, type Spring2D, type Spring2DPosition, type Spring2DTarget, type Spring2DWithOffset, type SpringConfig, type SpringOptions, type SpringPosition, type SpringState, type SpringSystem, type SpringSystemOptions, type SpringTarget, type SpringWithOffset, type TickerOptions, type Vector2, createSpringSystem, defineSpring };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { r as defineSpring, t as createSpringSystem } from "./system-Dxxv4ee7.js";
1
+ import { r as defineSpring, t as createSpringSystem } from "./system-BD52hyd8.js";
2
2
  export { createSpringSystem, defineSpring };
package/dist/nuxt.d.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  import { NuxtModule } from "@nuxt/schema";
2
2
 
3
3
  //#region src/vue/nuxt.d.ts
4
+ interface CoilyModuleOptions {
5
+ debug?: boolean;
6
+ }
4
7
  declare const coilyModule: NuxtModule;
5
8
  //#endregion
6
- export { coilyModule as default };
9
+ export { CoilyModuleOptions, coilyModule as default };
package/dist/nuxt.js CHANGED
@@ -1,11 +1,12 @@
1
1
  import { addComponent, addImports, addPluginTemplate, defineNuxtModule } from "@nuxt/kit";
2
2
  //#region src/vue/nuxt.ts
3
- const pluginTemplate = `import { createSpringSystem } from 'coily'
3
+ function getPluginTemplate(options) {
4
+ return `import { createSpringSystem } from 'coily'
4
5
  import { provideSpringSystem } from 'coily/vue'
5
6
 
6
7
  export default defineNuxtPlugin((nuxtApp) => {
7
8
  nuxtApp.hook('app:created', (vueApp) => {
8
- const system = createSpringSystem()
9
+ const system = createSpringSystem(${options.debug ? "{ debug: true }" : ""})
9
10
  provideSpringSystem(system, vueApp)
10
11
 
11
12
  if(import.meta.client) {
@@ -13,25 +14,49 @@ export default defineNuxtPlugin((nuxtApp) => {
13
14
  }
14
15
  })
15
16
  })`;
17
+ }
16
18
  const coilyModule = defineNuxtModule({
17
19
  meta: {
18
20
  name: "coily",
19
21
  configKey: "coily"
20
22
  },
21
- async setup() {
23
+ defaults: { debug: false },
24
+ async setup(_options) {
22
25
  addPluginTemplate({
23
26
  name: "coily",
24
27
  filename: "coily.plugin.mjs",
25
28
  mode: "all",
26
- getContents: () => pluginTemplate
29
+ getContents: () => getPluginTemplate(_options)
27
30
  });
28
- addImports([{
29
- from: "coily/vue",
30
- name: "useSpring"
31
- }, {
32
- from: "coily",
33
- name: "defineSpring"
34
- }]);
31
+ addImports([
32
+ {
33
+ from: "coily/vue",
34
+ name: "useSpring"
35
+ },
36
+ {
37
+ from: "coily/vue",
38
+ name: "useSpring2D"
39
+ },
40
+ {
41
+ from: "coily/vue",
42
+ name: "SpringRef",
43
+ type: true
44
+ },
45
+ {
46
+ from: "coily/vue",
47
+ name: "SpringRef2D",
48
+ type: true
49
+ },
50
+ {
51
+ from: "coily",
52
+ name: "defineSpring"
53
+ },
54
+ {
55
+ from: "coily",
56
+ name: "SpringConfig",
57
+ type: true
58
+ }
59
+ ]);
35
60
  addComponent({
36
61
  name: "SpringValue",
37
62
  filePath: "coily/vue",
@@ -1,6 +1,11 @@
1
1
  //#region src/motion-set.ts
2
2
  var MotionSet = class {
3
3
  #motions = /* @__PURE__ */ new Set();
4
+ #debug;
5
+ #lastSize = 0;
6
+ constructor(debug = false) {
7
+ this.#debug = debug;
8
+ }
4
9
  add(motion) {
5
10
  this.#motions.add(motion);
6
11
  }
@@ -10,7 +15,11 @@ var MotionSet = class {
10
15
  tick(dt) {
11
16
  for (const motion of this.#motions) {
12
17
  motion.tick(dt);
13
- if (motion.resting) this.#motions.delete(motion);
18
+ if (motion.isResting) this.#motions.delete(motion);
19
+ }
20
+ if (this.#debug && this.#motions.size !== this.#lastSize) {
21
+ this.#lastSize = this.#motions.size;
22
+ console.log(`coily: ${this.#lastSize} active motions`);
14
23
  }
15
24
  }
16
25
  };
@@ -25,7 +34,13 @@ function invariant(condition, message) {
25
34
  /**
26
35
  * Immutable spring configuration.
27
36
  */
28
- var SpringConfig = class {
37
+ var SpringConfig = class SpringConfig {
38
+ static default = new SpringConfig({
39
+ dampingRatio: 1,
40
+ duration: 500
41
+ });
42
+ /** @internal Incremented by `SpringConfig.assign` to signal mutations. */
43
+ #version = 0;
29
44
  mass;
30
45
  tension;
31
46
  damping;
@@ -52,10 +67,10 @@ var SpringConfig = class {
52
67
  if (dampingRatio !== void 0) invariant(dampingRatio >= 0, "Damping ratio must be greater than or equal to 0");
53
68
  if (duration !== void 0) invariant(duration > 0, "Duration must be greater than 0");
54
69
  invariant(displacement !== 0, "Displacement must not be 0");
55
- invariant(precision > 0, "Precision must be greater than 0");
70
+ invariant(precision >= 0, "Precision must be greater than or equal to 0");
56
71
  this.precision = precision;
57
72
  this.precisionMultiplier = 10 ** precision;
58
- this.restingMagnitude = 1 / this.precisionMultiplier;
73
+ this.restingMagnitude = .5 / this.precisionMultiplier;
59
74
  const hasM = mass !== void 0;
60
75
  const hasK = tension !== void 0;
61
76
  const hasC = damping !== void 0;
@@ -80,7 +95,7 @@ var SpringConfig = class {
80
95
  this.tension = tension;
81
96
  this.damping = damping;
82
97
  } else if (hasZ && hasT) {
83
- const threshold = this.restingMagnitude;
98
+ const threshold = .005;
84
99
  const x0 = Math.abs(displacement);
85
100
  invariant(x0 > threshold, "displacement must be greater than the precision threshold");
86
101
  let sigmaCoeff;
@@ -107,6 +122,52 @@ var SpringConfig = class {
107
122
  this.criticalDamping = 2 * this.mass * this.naturalFrequency;
108
123
  this.dampingRatio = this.damping / this.criticalDamping;
109
124
  }
125
+ static version(config) {
126
+ return config.#version;
127
+ }
128
+ static assign(target, source) {
129
+ target.#version++;
130
+ const t = target;
131
+ t.mass = source.mass;
132
+ t.tension = source.tension;
133
+ t.damping = source.damping;
134
+ t.precision = source.precision;
135
+ t.naturalFrequency = source.naturalFrequency;
136
+ t.criticalDamping = source.criticalDamping;
137
+ t.dampingRatio = source.dampingRatio;
138
+ t.precisionMultiplier = source.precisionMultiplier;
139
+ t.restingMagnitude = source.restingMagnitude;
140
+ }
141
+ /**
142
+ * Analytically estimates the time remaining for a spring to come to rest,
143
+ * given the current displacement and velocity.
144
+ *
145
+ * Uses the exponential decay envelope common to all three damping regimes.
146
+ * The decay rate depends on the regime:
147
+ * - Underdamped (ζ < 1): σ = ζωₙ
148
+ * - Critically damped (ζ = 1): σ = ωₙ
149
+ * - Overdamped (ζ > 1): σ = (ζ - √(ζ²-1))ωₙ (the slower eigenvalue)
150
+ *
151
+ * The effective initial amplitude accounts for both displacement and velocity,
152
+ * since kinetic energy can convert to additional displacement:
153
+ * A₀ = |x₀| + |v₀| / ωₙ
154
+ *
155
+ * Settling time: t = ln(A₀ / threshold) / σ, with a 2× safety factor
156
+ * to account for polynomial terms in the critically damped solution.
157
+ *
158
+ * Returns the estimated time in milliseconds.
159
+ */
160
+ computeTimeRemaining(state) {
161
+ const { position, velocity } = state;
162
+ const a0 = Math.abs(position) + Math.abs(velocity) / this.naturalFrequency;
163
+ if (a0 <= this.restingMagnitude) return 0;
164
+ let sigma;
165
+ if (this.dampingRatio < 1) sigma = this.dampingRatio * this.naturalFrequency;
166
+ else if (this.dampingRatio === 1) sigma = this.naturalFrequency;
167
+ else sigma = (this.dampingRatio - Math.sqrt(this.dampingRatio * this.dampingRatio - 1)) * this.naturalFrequency;
168
+ if (sigma <= 0) return Infinity;
169
+ return 2 * Math.log(a0 / this.restingMagnitude) / sigma * 1e3;
170
+ }
110
171
  };
111
172
  function defineSpring(input) {
112
173
  return new SpringConfig(input);
@@ -174,9 +235,9 @@ var State = class {
174
235
  set velocity(value) {
175
236
  this.#velocity = value;
176
237
  }
177
- /** Uses raw (unrounded) values so resting detection isn't affected by output quantization. */
178
- get resting() {
179
- return Math.abs(this.#velocity) < this.#config.restingMagnitude && Math.abs(this.#position) < this.#config.restingMagnitude;
238
+ /** A spring is resting when its rounded position and velocity are both zero. */
239
+ get isResting() {
240
+ return this.position === 0 && this.velocity === 0;
180
241
  }
181
242
  };
182
243
  //#endregion
@@ -280,12 +341,15 @@ var Motion = class {
280
341
  #currentSolver = null;
281
342
  #needsUpdate = false;
282
343
  #needsReset = false;
344
+ #configVersion = 0;
345
+ #timeRemaining = 0;
283
346
  #emitter;
284
347
  constructor(config, position, velocity) {
285
348
  this.#config = config;
286
349
  this.#state = new State(config, position, velocity);
287
350
  this.#emitter = new Emitter();
288
351
  this.#updateSolver();
352
+ this.#timeRemaining = this.#config.computeTimeRemaining(this.#state);
289
353
  }
290
354
  get position() {
291
355
  return this.#state.position;
@@ -293,7 +357,7 @@ var Motion = class {
293
357
  set position(value) {
294
358
  this.#state.position = value;
295
359
  this.#needsReset = true;
296
- if (!this.#state.resting) this.#emitter.emit("start");
360
+ if (!this.#state.isResting) this.#emitter.emit("start");
297
361
  }
298
362
  get velocity() {
299
363
  return this.#state.velocity;
@@ -302,16 +366,26 @@ var Motion = class {
302
366
  this.#state.velocity = value;
303
367
  this.#needsReset = true;
304
368
  }
305
- get resting() {
306
- return this.#state.resting;
369
+ get timeRemaining() {
370
+ return this.#timeRemaining;
371
+ }
372
+ get isResting() {
373
+ return this.#state.isResting;
307
374
  }
308
375
  configure(config) {
309
376
  this.#config = config;
310
377
  this.#state.configure(config);
378
+ this.#configVersion = SpringConfig.version(config);
311
379
  this.#needsUpdate = true;
312
380
  }
313
381
  tick(dt, emit = true) {
314
382
  invariant(this.#currentSolver, "Cannot tick a disposed motion");
383
+ if (SpringConfig.version(this.#config) !== this.#configVersion) {
384
+ this.#state.configure(this.#config);
385
+ this.#configVersion = SpringConfig.version(this.#config);
386
+ this.#needsUpdate = true;
387
+ }
388
+ const needsTimeRemaining = this.#needsUpdate || this.#needsReset;
315
389
  if (this.#needsUpdate) {
316
390
  this.#updateSolver();
317
391
  this.#needsUpdate = false;
@@ -320,10 +394,15 @@ var Motion = class {
320
394
  this.#currentSolver.configure();
321
395
  this.#needsReset = false;
322
396
  }
397
+ if (needsTimeRemaining) this.#timeRemaining = this.#config.computeTimeRemaining(this.#state);
323
398
  this.#currentSolver.tick(dt);
399
+ this.#timeRemaining = Math.max(0, this.#timeRemaining - dt * 1e3);
324
400
  if (emit) {
325
401
  this.#emitter.emit("update");
326
- if (this.#state.resting) this.#emitter.emit("stop");
402
+ if (this.#state.isResting) {
403
+ this.#timeRemaining = 0;
404
+ this.#emitter.emit("stop");
405
+ }
327
406
  }
328
407
  }
329
408
  onUpdate(callback) {
@@ -358,37 +437,69 @@ var Motion = class {
358
437
  };
359
438
  //#endregion
360
439
  //#region src/spring.ts
440
+ function normalizeTarget(target) {
441
+ if (target instanceof Spring) return {
442
+ spring: target,
443
+ offset: 0
444
+ };
445
+ if (typeof target === "object" && target !== null && "spring" in target && target.spring instanceof Spring) return {
446
+ spring: target.spring,
447
+ offset: target.offset ?? 0
448
+ };
449
+ return null;
450
+ }
361
451
  var Spring = class {
362
452
  #target;
363
453
  #config;
364
454
  #motion;
365
455
  #motions;
456
+ #leader = null;
457
+ #offset = 0;
458
+ #ownsConfig;
459
+ #unsubLeader = null;
366
460
  constructor(motions, position, config) {
367
- let target;
461
+ this.#motions = motions;
462
+ let numericTarget;
368
463
  let value;
464
+ let normalized = null;
369
465
  if (typeof position === "number") {
370
- target = position;
466
+ numericTarget = position;
371
467
  value = position;
372
468
  } else {
373
- target = position.target ?? position.value ?? 0;
374
- value = position.value ?? target;
469
+ normalized = normalizeTarget(position.target);
470
+ if (normalized) {
471
+ numericTarget = normalized.spring.#target + normalized.spring.#motion.position + normalized.offset;
472
+ value = position.value ?? numericTarget;
473
+ } else {
474
+ numericTarget = position.target ?? position.value ?? 0;
475
+ value = position.value ?? numericTarget;
476
+ }
477
+ }
478
+ const hasOverride = config !== void 0;
479
+ this.#ownsConfig = hasOverride || !normalized;
480
+ if (normalized && !hasOverride) this.#config = normalized.spring.#config;
481
+ else this.#config = config ?? SpringConfig.default;
482
+ this.#target = numericTarget;
483
+ this.#motion = new Motion(this.#config, value - numericTarget, 0);
484
+ if (!this.#motion.isResting) this.#motions.add(this.#motion);
485
+ if (normalized) {
486
+ this.#leader = normalized.spring;
487
+ this.#offset = normalized.offset;
488
+ this.#unsubLeader = normalized.spring.onUpdate(() => {
489
+ this.#setTarget(this.#leader.#target + this.#leader.#motion.position + this.#offset);
490
+ });
375
491
  }
376
- this.#target = target;
377
- this.#config = config;
378
- this.#motion = new Motion(config, value - target, 0);
379
- this.#motions = motions;
380
- if (!this.#motion.resting) this.#motions.add(this.#motion);
381
492
  }
382
493
  get target() {
383
494
  return this.#target;
384
495
  }
385
496
  set target(value) {
386
- if (value !== this.#target) {
387
- this.#motions.add(this.#motion);
388
- const currentValue = this.value;
389
- this.#target = value;
390
- this.#motion.position = currentValue - this.#target;
391
- this.#motion.tick(0);
497
+ if (typeof value === "number") {
498
+ this.#unfollow();
499
+ this.#setTarget(value);
500
+ } else {
501
+ const normalized = normalizeTarget(value);
502
+ this.#follow(normalized.spring, normalized.offset);
392
503
  }
393
504
  }
394
505
  get value() {
@@ -409,6 +520,9 @@ var Spring = class {
409
520
  this.#motions.add(this.#motion);
410
521
  this.#motion.velocity = value;
411
522
  }
523
+ get config() {
524
+ return this.#config;
525
+ }
412
526
  get mass() {
413
527
  return this.#config.mass;
414
528
  }
@@ -424,13 +538,45 @@ var Spring = class {
424
538
  get precision() {
425
539
  return this.#config.precision;
426
540
  }
427
- get resting() {
428
- return this.#motion.resting;
541
+ set config(value) {
542
+ if (value) {
543
+ if (this.#leader && !this.#ownsConfig) {
544
+ this.#ownsConfig = true;
545
+ this.#config = new SpringConfig({
546
+ tension: value.tension,
547
+ damping: value.damping,
548
+ mass: value.mass,
549
+ precision: value.precision
550
+ });
551
+ this.#motion.configure(this.#config);
552
+ } else SpringConfig.assign(this.#config, value);
553
+ this.#motions.add(this.#motion);
554
+ } else if (this.#ownsConfig) {
555
+ this.#ownsConfig = false;
556
+ this.#config = this.#leader ? this.#leader.#config : SpringConfig.default;
557
+ this.#motion.configure(this.#config);
558
+ this.#motions.add(this.#motion);
559
+ }
429
560
  }
430
- configure(config) {
431
- this.#config = config;
432
- this.#motion.configure(config);
433
- this.#motions.add(this.#motion);
561
+ get timeRemaining() {
562
+ return this.#motion.timeRemaining;
563
+ }
564
+ get isResting() {
565
+ return this.#motion.isResting;
566
+ }
567
+ jumpTo(value) {
568
+ this.#target = value;
569
+ this.#motion.position = 0;
570
+ this.#motion.velocity = 0;
571
+ this.#motion.tick(0);
572
+ }
573
+ dispose() {
574
+ if (this.#unsubLeader) {
575
+ this.#unsubLeader();
576
+ this.#unsubLeader = null;
577
+ }
578
+ this.#motions.remove(this.#motion);
579
+ this.#motion.dispose();
434
580
  }
435
581
  onUpdate(callback) {
436
582
  return this.#motion.onUpdate(callback);
@@ -441,15 +587,230 @@ var Spring = class {
441
587
  onStop(callback) {
442
588
  return this.#motion.onStop(callback);
443
589
  }
590
+ #setTarget(value) {
591
+ if (value !== this.#target) {
592
+ this.#motions.add(this.#motion);
593
+ const rawValue = this.#target + this.#motion.position;
594
+ this.#target = value;
595
+ this.#motion.position = rawValue - this.#target;
596
+ this.#motion.tick(0);
597
+ }
598
+ }
599
+ #follow(leader, offset) {
600
+ if (this.#unsubLeader) this.#unsubLeader();
601
+ this.#leader = leader;
602
+ this.#offset = offset;
603
+ if (!this.#ownsConfig) {
604
+ this.#config = leader.#config;
605
+ this.#motion.configure(this.#config);
606
+ }
607
+ this.#unsubLeader = leader.onUpdate(() => {
608
+ this.#setTarget(this.#leader.#target + this.#leader.#motion.position + this.#offset);
609
+ });
610
+ this.#setTarget(leader.#target + leader.#motion.position + offset);
611
+ }
612
+ #unfollow() {
613
+ if (!this.#leader) return;
614
+ this.#unsubLeader();
615
+ this.#unsubLeader = null;
616
+ this.#leader = null;
617
+ this.#offset = 0;
618
+ if (!this.#ownsConfig) {
619
+ this.#ownsConfig = true;
620
+ this.#config = new SpringConfig({
621
+ tension: this.#config.tension,
622
+ damping: this.#config.damping,
623
+ mass: this.#config.mass,
624
+ precision: this.#config.precision
625
+ });
626
+ this.#motion.configure(this.#config);
627
+ }
628
+ }
629
+ };
630
+ //#endregion
631
+ //#region src/vector2.ts
632
+ function isVector2(value) {
633
+ return typeof value === "object" && value !== null && "x" in value && typeof value.x === "number";
634
+ }
635
+ //#endregion
636
+ //#region src/spring2d.ts
637
+ function normalizeTarget2D(target) {
638
+ if (target instanceof Spring2D) return {
639
+ spring: target,
640
+ offset: void 0
641
+ };
642
+ if (typeof target === "object" && target !== null && "spring" in target && target.spring instanceof Spring2D) return {
643
+ spring: target.spring,
644
+ offset: target.offset
645
+ };
646
+ return null;
647
+ }
648
+ function toScalarTarget(spring, offset) {
649
+ return offset ? {
650
+ spring,
651
+ offset
652
+ } : spring;
653
+ }
654
+ function toScalarPos(target, value) {
655
+ if (typeof target === "number") return value !== void 0 ? {
656
+ target,
657
+ value
658
+ } : target;
659
+ return {
660
+ target,
661
+ value
662
+ };
663
+ }
664
+ var Spring2D = class Spring2D {
665
+ #x;
666
+ #y;
667
+ #value = {
668
+ x: 0,
669
+ y: 0
670
+ };
671
+ #velocity = {
672
+ x: 0,
673
+ y: 0
674
+ };
675
+ constructor(motions, position, config) {
676
+ let xPos;
677
+ let yPos;
678
+ if (isVector2(position)) {
679
+ xPos = position.x;
680
+ yPos = position.y;
681
+ } else {
682
+ const normalized = normalizeTarget2D(position.target);
683
+ if (normalized) {
684
+ const lx = normalized.spring.#x;
685
+ const ly = normalized.spring.#y;
686
+ const xTarget = toScalarTarget(lx, normalized.offset?.x);
687
+ const yTarget = toScalarTarget(ly, normalized.offset?.y);
688
+ xPos = toScalarPos(xTarget, position.value?.x);
689
+ yPos = toScalarPos(yTarget, position.value?.y);
690
+ } else if (isVector2(position.target)) {
691
+ const t = position.target;
692
+ xPos = {
693
+ target: t.x,
694
+ value: position.value?.x
695
+ };
696
+ yPos = {
697
+ target: t.y,
698
+ value: position.value?.y
699
+ };
700
+ } else {
701
+ xPos = {
702
+ target: position.target,
703
+ value: position.value?.x
704
+ };
705
+ yPos = {
706
+ target: void 0,
707
+ value: position.value?.y
708
+ };
709
+ }
710
+ }
711
+ this.#x = new Spring(motions, xPos, config);
712
+ this.#y = new Spring(motions, yPos, config);
713
+ }
714
+ get target() {
715
+ return {
716
+ x: this.#x.target,
717
+ y: this.#y.target
718
+ };
719
+ }
720
+ set target(value) {
721
+ if (value instanceof Spring2D) {
722
+ this.#x.target = value.#x;
723
+ this.#y.target = value.#y;
724
+ } else if ("spring" in value && value.spring instanceof Spring2D) {
725
+ const { spring, offset } = value;
726
+ this.#x.target = toScalarTarget(spring.#x, offset?.x);
727
+ this.#y.target = toScalarTarget(spring.#y, offset?.y);
728
+ } else {
729
+ const v = value;
730
+ this.#x.target = v.x;
731
+ this.#y.target = v.y;
732
+ }
733
+ }
734
+ get value() {
735
+ this.#value.x = this.#x.value;
736
+ this.#value.y = this.#y.value;
737
+ return this.#value;
738
+ }
739
+ set value(value) {
740
+ this.#x.value = value.x;
741
+ this.#y.value = value.y;
742
+ }
743
+ get velocity() {
744
+ this.#velocity.x = this.#x.velocity;
745
+ this.#velocity.y = this.#y.velocity;
746
+ return this.#velocity;
747
+ }
748
+ set velocity(value) {
749
+ this.#x.velocity = value.x;
750
+ this.#y.velocity = value.y;
751
+ }
752
+ get config() {
753
+ return this.#x.config;
754
+ }
755
+ get mass() {
756
+ return this.#x.mass;
757
+ }
758
+ get tension() {
759
+ return this.#x.tension;
760
+ }
761
+ get damping() {
762
+ return this.#x.damping;
763
+ }
764
+ get dampingRatio() {
765
+ return this.#x.dampingRatio;
766
+ }
767
+ get precision() {
768
+ return this.#x.precision;
769
+ }
770
+ set config(value) {
771
+ this.#x.config = value;
772
+ this.#y.config = value;
773
+ }
774
+ get timeRemaining() {
775
+ return Math.max(this.#x.timeRemaining, this.#y.timeRemaining);
776
+ }
777
+ get isResting() {
778
+ return this.#x.isResting && this.#y.isResting;
779
+ }
444
780
  jumpTo(value) {
445
- this.#target = value;
446
- this.#motion.position = 0;
447
- this.#motion.velocity = 0;
448
- this.#motion.tick(0);
781
+ this.#x.jumpTo(value.x);
782
+ this.#y.jumpTo(value.y);
449
783
  }
450
784
  dispose() {
451
- this.#motions.remove(this.#motion);
452
- this.#motion.dispose();
785
+ this.#x.dispose();
786
+ this.#y.dispose();
787
+ }
788
+ onUpdate(callback) {
789
+ const a = this.#x.onUpdate(callback);
790
+ const b = this.#y.onUpdate(callback);
791
+ return () => {
792
+ a();
793
+ b();
794
+ };
795
+ }
796
+ onStart(callback) {
797
+ const a = this.#x.onStart(callback);
798
+ const b = this.#y.onStart(callback);
799
+ return () => {
800
+ a();
801
+ b();
802
+ };
803
+ }
804
+ onStop(callback) {
805
+ const check = () => {
806
+ if (this.isResting) callback();
807
+ };
808
+ const a = this.#x.onStop(check);
809
+ const b = this.#y.onStop(check);
810
+ return () => {
811
+ a();
812
+ b();
813
+ };
453
814
  }
454
815
  };
455
816
  //#endregion
@@ -570,12 +931,15 @@ var SpringSystemImpl = class {
570
931
  #motion;
571
932
  #ticker;
572
933
  constructor(options) {
573
- this.#motion = new MotionSet();
934
+ this.#motion = new MotionSet(options?.debug);
574
935
  this.#ticker = new Ticker(this.#motion, options);
575
936
  }
576
937
  createSpring(position, config) {
577
938
  return new Spring(this.#motion, position, config);
578
939
  }
940
+ createSpring2D(position, config) {
941
+ return new Spring2D(this.#motion, position, config);
942
+ }
579
943
  advance(dt) {
580
944
  this.#motion.tick(dt / 1e3);
581
945
  }