@vectojs/core 0.1.0 → 0.2.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.
Files changed (46) hide show
  1. package/dist/animation/drivers.d.ts +48 -0
  2. package/dist/animation/easing.d.ts +16 -0
  3. package/dist/{chunk-M2IZPGOL.mjs → chunk-H3QIE77O.mjs} +316 -12
  4. package/dist/{chunk-53DAQC3U.js → chunk-LA3FJLP2.js} +369 -65
  5. package/dist/components/GridTextEntity.d.ts +15 -0
  6. package/dist/components/SplineEntity.d.ts +144 -0
  7. package/dist/components/TextEntity.d.ts +35 -0
  8. package/dist/index.d.ts +26 -577
  9. package/dist/index.js +121 -136
  10. package/dist/index.mjs +19 -34
  11. package/dist/{layout.d.mts → layout/LayoutEngine.d.ts} +15 -70
  12. package/dist/layout/LayoutWorker.d.ts +23 -0
  13. package/dist/layout/LayoutWorkerManager.d.ts +22 -0
  14. package/dist/layout/LayoutWorkerSource.d.ts +1 -0
  15. package/dist/layout/index.d.ts +3 -0
  16. package/dist/layout/measure.d.ts +20 -0
  17. package/dist/math/SpatialHashGrid.d.ts +53 -0
  18. package/dist/math/SpringPhysics.d.ts +13 -0
  19. package/dist/renderer/CanvasRenderer.d.ts +81 -0
  20. package/dist/renderer/IRenderer.d.ts +178 -0
  21. package/dist/renderer/SVGRenderer.d.ts +69 -0
  22. package/dist/renderer/WebGLPointRenderer.d.ts +62 -0
  23. package/dist/renderer/WebGPUParticleSystemManager.d.ts +14 -0
  24. package/dist/renderer/colorParse.d.ts +17 -0
  25. package/dist/renderer/index.d.ts +6 -0
  26. package/dist/text/ArabicShaper.d.ts +10 -0
  27. package/dist/text/BidiResolver.d.ts +6 -0
  28. package/dist/{text.d.ts → text/MSDFFont.d.ts} +10 -82
  29. package/dist/text/MSDFTextEntity.d.ts +30 -0
  30. package/dist/text/SVGEntity.d.ts +22 -0
  31. package/dist/text/index.d.ts +5 -0
  32. package/dist/text.js +2 -2
  33. package/dist/text.mjs +1 -1
  34. package/dist/tree/ComputeParticleEntity.d.ts +118 -0
  35. package/dist/tree/DOMPortalEntity.d.ts +18 -0
  36. package/dist/{Entity-D-rfAFCf.d.mts → tree/Entity.d.ts} +71 -201
  37. package/dist/tree/Scene.d.ts +302 -0
  38. package/package.json +5 -5
  39. package/dist/Entity-D-rfAFCf.d.ts +0 -572
  40. package/dist/index-ByBDSmMK.d.mts +0 -365
  41. package/dist/index-C3Fd_XmG.d.ts +0 -365
  42. package/dist/index.d.mts +0 -577
  43. package/dist/layout.d.ts +0 -319
  44. package/dist/renderer.d.mts +0 -2
  45. package/dist/renderer.d.ts +0 -2
  46. package/dist/text.d.mts +0 -201
@@ -0,0 +1,48 @@
1
+ import { type EasingFn, type EasingName } from './easing';
2
+ export interface SpringConfig {
3
+ stiffness?: number;
4
+ damping?: number;
5
+ mass?: number;
6
+ }
7
+ export interface TweenConfig {
8
+ duration: number;
9
+ easing?: EasingName | EasingFn;
10
+ delay?: number;
11
+ }
12
+ /** A motion config. Presence of `duration` selects a tween; otherwise a spring. */
13
+ export type MotionConfig = 'spring' | SpringConfig | TweenConfig;
14
+ export declare function isTweenConfig(c: MotionConfig): c is TweenConfig;
15
+ /** Backs one animating property. Ticked in ms; writes `value`. */
16
+ export interface PropertyDriver {
17
+ value: number;
18
+ /** The current destination — applied exactly when the animation completes, so a
19
+ * finished spring lands on target rather than within its rest epsilon. */
20
+ readonly target: number;
21
+ /** Change the destination. Spring keeps velocity; tween restarts from current value. */
22
+ retarget(to: number): void;
23
+ tick(dtMs: number): void;
24
+ isDone(): boolean;
25
+ }
26
+ export declare class TweenDriver implements PropertyDriver {
27
+ value: number;
28
+ private from;
29
+ private to;
30
+ private elapsed;
31
+ private readonly duration;
32
+ private readonly delay;
33
+ private readonly ease;
34
+ constructor(from: number, to: number, cfg: TweenConfig);
35
+ get target(): number;
36
+ retarget(to: number): void;
37
+ tick(dtMs: number): void;
38
+ isDone(): boolean;
39
+ }
40
+ export declare class SpringDriver implements PropertyDriver {
41
+ private spring;
42
+ constructor(from: number, to: number, cfg: SpringConfig);
43
+ get value(): number;
44
+ get target(): number;
45
+ retarget(to: number): void;
46
+ tick(dtMs: number): void;
47
+ isDone(): boolean;
48
+ }
@@ -0,0 +1,16 @@
1
+ /** An easing function: maps normalized time t in [0,1] to eased progress. */
2
+ export type EasingFn = (t: number) => number;
3
+ /** Curated easing set. Add sparingly — every entry must map f(0)=0, f(1)=1. */
4
+ export declare const Easing: {
5
+ linear: (t: number) => number;
6
+ easeInQuad: (t: number) => number;
7
+ easeOutQuad: (t: number) => number;
8
+ easeInOutQuad: (t: number) => number;
9
+ easeInCubic: (t: number) => number;
10
+ easeOutCubic: (t: number) => number;
11
+ easeInOutCubic: (t: number) => number;
12
+ easeOutBack: (t: number) => number;
13
+ easeInOutBack: (t: number) => number;
14
+ };
15
+ /** Name of a built-in easing curve. */
16
+ export type EasingName = keyof typeof Easing;
@@ -2,6 +2,120 @@ import {
2
2
  LayoutWorkerManager
3
3
  } from "./chunk-YA2J5ZH7.mjs";
4
4
 
5
+ // src/math/SpringPhysics.ts
6
+ var SpringPhysics = class {
7
+ value;
8
+ target;
9
+ velocity = 0;
10
+ stiffness = 180;
11
+ damping = 12;
12
+ mass = 1;
13
+ valEpsilon = 5e-3;
14
+ velEpsilon = 5e-3;
15
+ constructor(initial) {
16
+ this.value = initial;
17
+ this.target = initial;
18
+ }
19
+ update(dt) {
20
+ if (this.isAtRest()) {
21
+ this.value = this.target;
22
+ this.velocity = 0;
23
+ return;
24
+ }
25
+ const forceSpring = -this.stiffness * (this.value - this.target);
26
+ const forceDamping = -this.damping * this.velocity;
27
+ const acceleration = (forceSpring + forceDamping) / this.mass;
28
+ this.velocity += acceleration * dt;
29
+ this.value += this.velocity * dt;
30
+ }
31
+ isAtRest() {
32
+ return Math.abs(this.value - this.target) < this.valEpsilon && Math.abs(this.velocity) < this.velEpsilon;
33
+ }
34
+ };
35
+
36
+ // src/animation/easing.ts
37
+ var c1 = 1.70158;
38
+ var c3 = c1 + 1;
39
+ var Easing = {
40
+ linear: (t) => t,
41
+ easeInQuad: (t) => t * t,
42
+ easeOutQuad: (t) => t * (2 - t),
43
+ easeInOutQuad: (t) => t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2,
44
+ easeInCubic: (t) => t * t * t,
45
+ easeOutCubic: (t) => 1 - Math.pow(1 - t, 3),
46
+ easeInOutCubic: (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2,
47
+ easeOutBack: (t) => 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2),
48
+ easeInOutBack: (t) => {
49
+ const c2 = c1 * 1.525;
50
+ return t < 0.5 ? Math.pow(2 * t, 2) * ((c2 + 1) * 2 * t - c2) / 2 : (Math.pow(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2;
51
+ }
52
+ };
53
+
54
+ // src/animation/drivers.ts
55
+ function isTweenConfig(c) {
56
+ return typeof c === "object" && "duration" in c;
57
+ }
58
+ var TweenDriver = class {
59
+ value;
60
+ from;
61
+ to;
62
+ elapsed = 0;
63
+ duration;
64
+ delay;
65
+ ease;
66
+ constructor(from, to, cfg) {
67
+ this.value = from;
68
+ this.from = from;
69
+ this.to = to;
70
+ this.duration = Math.max(1, cfg.duration);
71
+ this.delay = cfg.delay ?? 0;
72
+ this.ease = typeof cfg.easing === "function" ? cfg.easing : Easing[cfg.easing ?? "easeOutQuad"];
73
+ }
74
+ get target() {
75
+ return this.to;
76
+ }
77
+ retarget(to) {
78
+ this.from = this.value;
79
+ this.to = to;
80
+ this.elapsed = 0;
81
+ }
82
+ tick(dtMs) {
83
+ this.elapsed += dtMs;
84
+ const active = this.elapsed - this.delay;
85
+ if (active <= 0) return;
86
+ const p = Math.min(active / this.duration, 1);
87
+ this.value = this.from + (this.to - this.from) * this.ease(p);
88
+ }
89
+ isDone() {
90
+ return this.elapsed - this.delay >= this.duration;
91
+ }
92
+ };
93
+ var SpringDriver = class {
94
+ spring;
95
+ constructor(from, to, cfg) {
96
+ this.spring = new SpringPhysics(from);
97
+ if (cfg.stiffness !== void 0) this.spring.stiffness = cfg.stiffness;
98
+ if (cfg.damping !== void 0) this.spring.damping = cfg.damping;
99
+ if (cfg.mass !== void 0) this.spring.mass = cfg.mass;
100
+ this.spring.target = to;
101
+ }
102
+ get value() {
103
+ return this.spring.value;
104
+ }
105
+ get target() {
106
+ return this.spring.target;
107
+ }
108
+ retarget(to) {
109
+ this.spring.target = to;
110
+ }
111
+ tick(dtMs) {
112
+ this.spring.update(dtMs / 1e3);
113
+ }
114
+ isDone() {
115
+ return this.spring.isAtRest();
116
+ }
117
+ };
118
+
5
119
  // src/tree/Entity.ts
6
120
  var VectoJSEvent = class {
7
121
  /** The event name. */
@@ -80,12 +194,60 @@ var Entity = class {
80
194
  if (this._scene) return this._scene;
81
195
  return this.parent ? this.parent.scene : null;
82
196
  }
83
- x = 0;
84
- y = 0;
85
- scaleX = 1;
86
- scaleY = 1;
87
- rotation = 0;
88
- opacity = 1;
197
+ _x = 0;
198
+ _y = 0;
199
+ _scaleX = 1;
200
+ _scaleY = 1;
201
+ _rotation = 0;
202
+ _opacity = 1;
203
+ // Fast-path flag: false for the overwhelming majority of entities (incl. the
204
+ // Danmaku hot loop), so a bare `entity.x = v` is one boolean check + field write.
205
+ _hasTransitions = false;
206
+ _transitions = null;
207
+ _drivers = /* @__PURE__ */ new Map();
208
+ _mounted = false;
209
+ get x() {
210
+ return this._x;
211
+ }
212
+ set x(v) {
213
+ if (this._hasTransitions) this._animateProp("x", v);
214
+ else this._x = v;
215
+ }
216
+ get y() {
217
+ return this._y;
218
+ }
219
+ set y(v) {
220
+ if (this._hasTransitions) this._animateProp("y", v);
221
+ else this._y = v;
222
+ }
223
+ get scaleX() {
224
+ return this._scaleX;
225
+ }
226
+ set scaleX(v) {
227
+ if (this._hasTransitions) this._animateProp("scaleX", v);
228
+ else this._scaleX = v;
229
+ }
230
+ get scaleY() {
231
+ return this._scaleY;
232
+ }
233
+ set scaleY(v) {
234
+ if (this._hasTransitions) this._animateProp("scaleY", v);
235
+ else this._scaleY = v;
236
+ }
237
+ get rotation() {
238
+ return this._rotation;
239
+ }
240
+ set rotation(v) {
241
+ if (this._hasTransitions) this._animateProp("rotation", v);
242
+ else this._rotation = v;
243
+ }
244
+ get opacity() {
245
+ return this._opacity;
246
+ }
247
+ set opacity(v) {
248
+ if (this._hasTransitions) this._animateProp("opacity", v);
249
+ else this._opacity = v;
250
+ }
89
251
  isDOMPortal = false;
90
252
  _interactive = false;
91
253
  get interactive() {
@@ -140,9 +302,20 @@ var Entity = class {
140
302
  if (s) {
141
303
  s.a11yNeedsReorder = true;
142
304
  s.markDirty();
305
+ child._notifyMounted();
143
306
  }
144
307
  return this;
145
308
  }
309
+ /** Called once when this entity becomes attached to a live Scene. Override to react. */
310
+ onMounted() {
311
+ }
312
+ /** Fire onMounted for this node and its descendants, guarded against double-fire. */
313
+ _notifyMounted() {
314
+ if (this._mounted) return;
315
+ this._mounted = true;
316
+ this.onMounted();
317
+ for (const c of this.children) c._notifyMounted();
318
+ }
146
319
  /**
147
320
  * Remove a child entity from this node.
148
321
  *
@@ -195,6 +368,123 @@ var Entity = class {
195
368
  });
196
369
  return this;
197
370
  }
371
+ /** Write a driver-computed value to a backing field without re-triggering the setter. */
372
+ _applyAnimated(prop, v) {
373
+ switch (prop) {
374
+ case "x":
375
+ this._x = v;
376
+ break;
377
+ case "y":
378
+ this._y = v;
379
+ break;
380
+ case "scaleX":
381
+ this._scaleX = v;
382
+ break;
383
+ case "scaleY":
384
+ this._scaleY = v;
385
+ break;
386
+ case "rotation":
387
+ this._rotation = v;
388
+ break;
389
+ case "opacity":
390
+ this._opacity = v;
391
+ break;
392
+ }
393
+ }
394
+ _currentOf(prop) {
395
+ switch (prop) {
396
+ case "x":
397
+ return this._x;
398
+ case "y":
399
+ return this._y;
400
+ case "scaleX":
401
+ return this._scaleX;
402
+ case "scaleY":
403
+ return this._scaleY;
404
+ case "rotation":
405
+ return this._rotation;
406
+ case "opacity":
407
+ return this._opacity;
408
+ }
409
+ }
410
+ /**
411
+ * Write a value immediately, bypassing any configured transition. For subclasses
412
+ * that need to seed a starting state (e.g. the presence helper's enter `from`).
413
+ */
414
+ setImmediate(prop, v) {
415
+ this._drivers.delete(prop);
416
+ this._applyAnimated(prop, v);
417
+ }
418
+ _spawnDriver(prop, to, cfg) {
419
+ if (prop !== "opacity" && this.scene?.prefersReducedMotion) {
420
+ this._drivers.delete(prop);
421
+ this._applyAnimated(prop, to);
422
+ return;
423
+ }
424
+ const existing = this._drivers.get(prop);
425
+ if (existing) {
426
+ existing.retarget(to);
427
+ return;
428
+ }
429
+ const from = this._currentOf(prop);
430
+ const driver = isTweenConfig(cfg) ? new TweenDriver(from, to, cfg) : new SpringDriver(from, to, cfg === "spring" ? {} : cfg);
431
+ this._drivers.set(prop, driver);
432
+ this.scene?.markDirty();
433
+ }
434
+ /** Assignment path when a declarative transition is configured for `prop`. */
435
+ _animateProp(prop, to) {
436
+ const cfg = this._transitions?.get(prop);
437
+ if (!cfg) {
438
+ this._applyAnimated(prop, to);
439
+ return;
440
+ }
441
+ this._spawnDriver(prop, to, cfg);
442
+ }
443
+ /** Declare which properties animate, and how. Subsequent assignment animates them. */
444
+ setTransition(config) {
445
+ this._transitions ??= /* @__PURE__ */ new Map();
446
+ for (const [k, v] of Object.entries(config))
447
+ this._transitions.set(k, v);
448
+ this._hasTransitions = this._transitions.size > 0;
449
+ return this;
450
+ }
451
+ /** Imperative tween toward targets; resolves when all reach their end. */
452
+ animateTo(props, cfg) {
453
+ return this._driveTo(props, cfg);
454
+ }
455
+ /** Imperative spring toward targets; resolves when all reach rest. */
456
+ springTo(props, cfg = {}) {
457
+ return this._driveTo(props, cfg);
458
+ }
459
+ _driveTo(props, cfg) {
460
+ const entries = Object.entries(props);
461
+ return Promise.all(
462
+ entries.map(
463
+ (e) => new Promise((resolve) => {
464
+ this._spawnDriver(e[0], e[1], cfg);
465
+ const d = this._drivers.get(e[0]);
466
+ if (!d)
467
+ resolve();
468
+ else d.onDone = resolve;
469
+ })
470
+ )
471
+ ).then(() => void 0);
472
+ }
473
+ /** Advance active property drivers one frame. Call from update(). */
474
+ tickDrivers(dt) {
475
+ if (this._drivers.size === 0) return;
476
+ for (const [prop, driver] of this._drivers) {
477
+ driver.tick(dt);
478
+ if (driver.isDone()) {
479
+ this._applyAnimated(prop, driver.target);
480
+ driver.onDone?.();
481
+ this._drivers.delete(prop);
482
+ } else {
483
+ this._applyAnimated(prop, driver.value);
484
+ }
485
+ }
486
+ this.scene?.markDirty();
487
+ }
198
488
  /**
199
489
  * Advance the entity's internal state for one frame.
200
490
  *
@@ -204,7 +494,8 @@ var Entity = class {
204
494
  * @param dt - Elapsed time since the last frame in milliseconds.
205
495
  * @param time - Absolute timestamp from `performance.now()`.
206
496
  */
207
- update(_dt, time) {
497
+ update(dt, time) {
498
+ this.tickDrivers(dt);
208
499
  if (this.animations.length > 0) {
209
500
  const anim = this.animations[0];
210
501
  if (anim.startTime === -1) {
@@ -438,15 +729,23 @@ var Entity = class {
438
729
  return null;
439
730
  }
440
731
  /**
441
- * Whether this entity still has a queued/running tween animation.
732
+ * Whether this entity still has a queued/running tween animation, or an
733
+ * active {@link setTransition}/{@link animateTo}/{@link springTo} property
734
+ * driver.
442
735
  *
443
- * Used by {@link Scene}'s `onDemand` render mode to keep redrawing while an
444
- * animation is in flight.
736
+ * Used by {@link Scene} to keep rendering continuously while an animation
737
+ * is in flight — both in `onDemand` render mode, and to hold off the
738
+ * `always`-mode idle auto-throttle. Without checking `_drivers` here, a
739
+ * property driver becomes invisible to that throttle: `markDirty()` called
740
+ * from inside `update()`/`tickDrivers()` is wiped by the loop's own
741
+ * `dirty = false` at the end of that same tick, so once the throttle
742
+ * engages an in-flight spring/tween only advances one animation-frame per
743
+ * external `markDirty()` trigger instead of every render frame.
445
744
  *
446
- * @returns `true` if at least one animation remains.
745
+ * @returns `true` if at least one animation or property driver remains.
447
746
  */
448
747
  hasPendingAnimations() {
449
- return this.animations.length > 0;
748
+ return this.animations.length > 0 || this._drivers.size > 0;
450
749
  }
451
750
  };
452
751
 
@@ -899,6 +1198,11 @@ var SVGEntity = class extends Entity {
899
1198
  };
900
1199
 
901
1200
  export {
1201
+ SpringPhysics,
1202
+ Easing,
1203
+ isTweenConfig,
1204
+ TweenDriver,
1205
+ SpringDriver,
902
1206
  VectoJSEvent,
903
1207
  Entity,
904
1208
  MSDFFont,