@weasel-js/animator 1.9.4 → 1.10.0

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/README.md CHANGED
@@ -23,14 +23,32 @@ animator.spring({ target, to: 40 }); // bends the motion; neither cancels the ot
23
23
 
24
24
  | Export | What it is |
25
25
  |---|---|
26
- | `useAnimator()` | The animator: `tween`, `spring`, `physics`, `decay`, `hold`, `loop`, `stagger`, `timeline`, and `watch`. The frame loop stops while its host is unseen. |
27
- | `createTarget(opts)` | One value an animator moves. Animations on a target add, so a second one bends the first. |
26
+ | `useAnimator()` | The animator: `tween`, `spring`, `physics`, `decay`, `hold`, `loop`, `stagger`, `timeline`, `watch`, and `live`, with `pause`, `resume`, and `setTimeScale` over everything it runs. The frame loop stops while its host is unseen. |
27
+ | `createTarget(opts)` | One value an animator moves. Animations on a target add, so a second one bends the first. `target.axisIndexes(['y'])` answers where the axes it names sit among the target's numbers. |
28
28
  | `createTargetTable(opts)` | Many like values as rows of flat arrays, shown in one pass. |
29
29
  | `sampleTrack`, the `Track` types | Keyframe tracks for `animator.timeline`. |
30
30
  | `resolveSkeleton`, `skeletonAxes`, `solveIk`, `mirrorPose` | Skeleton math: joints to matrices, a skeleton pose as a target's axes, two-bone IK. |
31
31
  | `tweenVertexColors` and its siblings, `ColorOverrideRegistry` | Per-vertex color animation, and the table a renderer reads the colors from. |
32
32
  | `createSlew`, `slewToward` | A value that follows another at a bounded rate. |
33
33
 
34
+ A hold or a timeline track over part of a target names the axes it holds, by the value's own
35
+ fields or by index. A rig's pose target names its axes `<joint>.<field>`, so a joint's name
36
+ reaches all five of its axes:
37
+
38
+ ```tsx
39
+ animator.hold({ target, value: { x: 0, y: 120 }, axes: ['y'] });
40
+ animator.hold({ target: rig.target, value: pose, axes: ['armL', 'foreL.rotation'] });
41
+ ```
42
+
43
+ `animator.stagger(items, delay)` answers a builder. Its `.tween` and `.spring` move the items
44
+ themselves where they are targets, a `target` named per item otherwise, and hand values to
45
+ `onTick` with neither; `.each` runs any factory.
46
+
47
+ ```tsx
48
+ animator.stagger(targets, 80).spring({ to: (target) => ({ ...target.committed(), y: 200 }), preset: 'wobbly' });
49
+ animator.stagger(rows, 80).tween({ target: (row) => row.target, to: 1, ms: 300 });
50
+ ```
51
+
34
52
  Moving a scene node's pose is `@weasel-js/core`'s half: `tweenPose`, `springPose`,
35
53
  `momentum`, `bindRig`, and `useAnimatedReflow` drive this animator against a scene. How
36
54
  animations combine on a target is in
package/dist/index.d.ts CHANGED
@@ -2,6 +2,17 @@ import { EasingSpec, SpringPresetName, EasingName, BezierEasing, GlMat3, Path, P
2
2
  export { BezierEasing, EasingFn, EasingSpec, SpringPreset, SpringPresetName } from '@weasel-js/geom';
3
3
  import { ColorSpace } from '@weasel-js/paint';
4
4
 
5
+ /**
6
+ * What an axis of a `T` goes by: a field of a plain object, or a path into a nested one, as a
7
+ * pose's `knee` and `knee.rotation` are. `never` for a number or an array, whose axes have
8
+ * indexes only.
9
+ */
10
+ type AxisName<T> = T extends number | readonly unknown[] ? never : T extends object ? {
11
+ [K in keyof T & string]: NonNullable<T[K]> extends number ? K : K | `${K}.${string}`;
12
+ }[keyof T & string] : string;
13
+ /** One of a target's axes: its index, or its name where the target's axes have names. */
14
+ type AxisRef<T = unknown> = number | AxisName<T>;
15
+
5
16
  /** How a target's value maps to the flat numbers an animation moves, and back. */
6
17
  interface TargetAxes<T> {
7
18
  count: number;
@@ -10,6 +21,9 @@ interface TargetAxes<T> {
10
21
  /** Indexes of axes that are angles in radians. An animation starting from one takes the short
11
22
  * way round to its destination. */
12
23
  angles?: readonly number[];
24
+ /** A name for each axis, in the order `to` answers them, for `axes` options to go by. A dot
25
+ * makes a path: `knee.rotation` is also reached as `knee`, with the joint's other axes. */
26
+ names?: readonly string[];
13
27
  /** Optional. `value` with `delta` added axis by axis: what `from` answers for `to(value)`
14
28
  * plus `delta`, built with no array between. A read of a moving target goes through it. */
15
29
  add?(value: T, delta: ArrayLike<number>): T;
@@ -57,6 +71,12 @@ interface Target<T> {
57
71
  readonly axisCount: number;
58
72
  /** How its value maps to numbers. */
59
73
  readonly axes: TargetAxes<T>;
74
+ /**
75
+ * The indexes of the axes `axes` names, in the order named: what a hold's or a track's `axes`
76
+ * resolves through. A name is one of `axes.names`, or a path over several of them; an index
77
+ * passes through. Throws on a name or an index the target has no axis for.
78
+ */
79
+ axisIndexes(axes: readonly AxisRef<T>[]): number[];
60
80
  /** The committed value: what shows once nothing is moving it. */
61
81
  committed(): T;
62
82
  /** Writes the committed value. Animations moving the target go on adding to the new one. */
@@ -173,8 +193,11 @@ interface SampledTrack<T> {
173
193
  interpolateName?: string;
174
194
  /** The track's share among the target's holds, as `HoldOptions.weight`. Default 1. */
175
195
  weight?: number | (() => number);
176
- /** The axes of the target the track holds, as `HoldOptions.axes`. Default: every axis. */
177
- axes?: readonly number[];
196
+ /** What a function `weight` goes by in a clip, as `targetName` is for `target`. */
197
+ weightName?: string;
198
+ /** The axes of the target the track holds, by name or by index, as `HoldOptions.axes`.
199
+ * Default: every axis. A clip carries them as they are written here. */
200
+ axes?: readonly AxisRef<T>[];
178
201
  }
179
202
  /** A clock that events are booked against, reading in ms. An `AudioEngine`
180
203
  * from `@weasel-js/audio` is one. */
@@ -349,18 +372,21 @@ interface TargetPhysicsOptions<T> extends Omit<TargetSpringOptions<T>, 'to'> {
349
372
  interface HoldOptions<T> {
350
373
  /** The value to hold. */
351
374
  target: Target<T>;
352
- /** What to show in place of the committed value: a value, or a function read each frame. */
375
+ /** What to show in place of the committed value: a value, or a function read each frame.
376
+ * A value is read as the hold starts and again at `touch()`, and costs a frame nothing. */
353
377
  value: T | (() => T);
354
378
  /** Its share among the target's holds, 0 to 1: a number, or a function read each frame.
355
379
  * Default 1. Holds on one target show their weighted average, and whatever their weights
356
380
  * leave short of 1 is the committed value's. */
357
381
  weight?: number | (() => number);
358
382
  /**
359
- * Indexes of the target's axes it holds, in the order the target's `TargetAxes.to` answers
360
- * them. The rest are left to the target's other holds and its committed value, and a release
361
- * commits only these. Default: every axis. For a rig's joints, `jointAxes`.
383
+ * The axes of the target it holds, each by name or by index: a plain object's fields
384
+ * (`['y']`), a rig's joints (`['armL', 'foreL']`) or one of a joint's own
385
+ * (`['armL.rotation']`), as `target.axisIndexes` resolves them when the hold starts. The
386
+ * rest are left to the target's other holds and its committed value, and a release commits
387
+ * only these. Default: every axis.
362
388
  */
363
- axes?: readonly number[];
389
+ axes?: readonly AxisRef<T>[];
364
390
  /** Any new animation passed the same cancelKey cancels this one. */
365
391
  cancelKey?: string;
366
392
  /** Names this hold in {@link AnimatorEvent}s and `animator.live()`. */
@@ -372,7 +398,8 @@ interface HoldOptions<T> {
372
398
  * does; pausing it or scaling its time changes nothing, since it has no clock. */
373
399
  interface HoldHandle extends AnimationHandle {
374
400
  /** Says a function-form `value` or `weight` changed outside a frame, so the target shows it
375
- * now rather than at the next frame the animator happens to run. */
401
+ * now rather than at the next frame the animator happens to run. A `value` given as an
402
+ * object and then written in place shows only once this is called. */
376
403
  touch(): void;
377
404
  /** Ends the hold. By default it commits the value it was showing, so the target stays where
378
405
  * it is; with `commit: false` the committed value is left alone and the target falls back
@@ -392,14 +419,112 @@ interface TargetDecayOptions<T> extends OnTarget<T> {
392
419
  threshold?: number;
393
420
  }
394
421
 
422
+ /** A stagger run's own options: the factory form's fourth argument, and part
423
+ * of every fluent builder method's options. */
424
+ interface StaggerOptions {
425
+ /** Cancel-key for the supervising registration. `animator.cancelKey(key)`
426
+ * cancels the whole stagger; `animator.isActive(key)` returns true while
427
+ * any timer or child is alive. */
428
+ cancelKey?: string;
429
+ /** Names this stagger in {@link AnimatorEvent}s and `animator.live()`. */
430
+ label?: string;
431
+ }
432
+ /** Per-index delay schedule. Number ⇒ `index * delay` ms. Function ⇒ caller
433
+ * decides the absolute delay for each index (e.g. `i => i * i * 30`). */
434
+ type StaggerDelay = number | ((index: number) => number);
435
+ /** Produces the animation for one staggered item. */
436
+ type StaggerFactory<TItem> = (item: TItem, index: number) => AnimationHandle;
437
+ /** A `T` value or a function that derives one from the per-item context. Used
438
+ * by the fluent builder methods (`.tween`, `.spring`) so each item can
439
+ * vary an option (e.g. `to: (_item, i) => (i + 1) * 10`). */
440
+ type StaggerPerItem<T, TItem> = T | ((item: TItem, index: number) => T);
441
+ /** Options for the stagger builder's `.tween` in its `onTick` form: a tween per item, where
442
+ * `from`, `to` and `ms` may each vary by item. `cancelKey` and `label` belong
443
+ * to the stagger as a whole, as in {@link StaggerOptions}. */
444
+ interface StaggerTweenOptions<T, TItem> extends StaggerOptions {
445
+ /** Absent: this form hands values to `onTick`. {@link StaggerTargetTweenOptions} moves targets. */
446
+ target?: undefined;
447
+ from: StaggerPerItem<T, TItem>;
448
+ to: StaggerPerItem<T, TItem>;
449
+ ms: StaggerPerItem<number, TItem>;
450
+ easing?: EasingSpec;
451
+ interpolate?: Interpolate<T>;
452
+ interpolator?: InterpolatorFactory<T>;
453
+ onTick: (value: T, item: TItem, index: number) => void;
454
+ onDone?: (item: TItem, index: number) => void;
455
+ }
456
+ /** Options for the stagger builder's `.spring` in its `onTick` form: a spring per item, where
457
+ * `from`, `to` and `velocity` may each vary by item. */
458
+ interface StaggerSpringOptions<T, TItem> extends StaggerOptions, Pick<SpringOptions<T>, 'preset' | 'stiffness' | 'damping' | 'mass' | 'interpolate' | 'add' | 'subtract' | 'scale' | 'magnitude' | 'restThreshold'> {
459
+ /** Absent: this form hands values to `onTick`. {@link StaggerTargetSpringOptions} moves targets. */
460
+ target?: undefined;
461
+ from: StaggerPerItem<T, TItem>;
462
+ to: StaggerPerItem<T, TItem>;
463
+ velocity?: StaggerPerItem<T, TItem>;
464
+ onTick: (value: T, item: TItem, index: number) => void;
465
+ onDone?: (item: TItem, index: number) => void;
466
+ }
467
+ /** What the stagger builder's methods share where each item's animation moves a {@link Target}. */
468
+ interface StaggerOnTarget<T, TItem> extends StaggerOptions {
469
+ /** The target an item's animation moves: one for them all, or a function of the item.
470
+ * Default: the item itself, where the items are targets. */
471
+ target?: StaggerPerItem<Target<T>, TItem>;
472
+ /** Told an item's animation's own value each frame, as `TargetTweenOptions.onTick` is. */
473
+ onTick?: (value: T, item: TItem, index: number) => void;
474
+ onDone?: (item: TItem, index: number) => void;
475
+ }
476
+ /** Options for the stagger builder's `.tween` moving targets: `animator.tween`'s target form
477
+ * per item, where `to`, `from` and `ms` may each vary by item. The tweens share no
478
+ * `cancelKey`, so each adds to whatever else is moving its target. */
479
+ interface StaggerTargetTweenOptions<T, TItem> extends StaggerOnTarget<T, TItem>, Pick<TargetTweenOptions<T>, 'easing' | 'interpolate' | 'interpolator'> {
480
+ to: StaggerPerItem<T, TItem>;
481
+ from?: StaggerPerItem<T, TItem>;
482
+ ms: StaggerPerItem<number, TItem>;
483
+ }
484
+ /** Options for the stagger builder's `.spring` moving targets: `animator.spring`'s target
485
+ * form per item, where `to`, `from` and `velocity` may each vary by item. */
486
+ interface StaggerTargetSpringOptions<T, TItem> extends StaggerOnTarget<T, TItem>, Pick<TargetSpringOptions<T>, 'preset' | 'stiffness' | 'damping' | 'mass' | 'restThreshold'> {
487
+ to: StaggerPerItem<T, TItem>;
488
+ from?: StaggerPerItem<T, TItem>;
489
+ velocity?: StaggerPerItem<T, TItem>;
490
+ }
491
+ /** `.tween`'s options where the items are targets, each moved by its own tween. No options
492
+ * fit where they are not. */
493
+ type StaggerItemTweenOptions<TItem> = TItem extends Target<infer T> ? StaggerTargetTweenOptions<T, TItem> : never;
494
+ /** `.spring`'s options where the items are targets, as {@link StaggerItemTweenOptions}. */
495
+ type StaggerItemSpringOptions<TItem> = TItem extends Target<infer T> ? StaggerTargetSpringOptions<T, TItem> : never;
496
+ /** Narrows a builder method's target options to the call that names its `target`. */
497
+ type Naming = {
498
+ target: NonNullable<unknown>;
499
+ };
500
+ /**
501
+ * Fluent form of `Animator.stagger`: pick what to run per item after the
502
+ * items and the delay schedule are already fixed.
503
+ *
504
+ * `.tween` and `.spring` each take three shapes of options. Where the items are targets, each
505
+ * item's animation moves the item: `animator.stagger(targets, 80).spring({ to: (t) => ... })`.
506
+ * With a `target`, it moves the target that names for the item. With neither, it hands its
507
+ * values to `onTick`.
508
+ */
509
+ interface StaggerBuilder<TItem> {
510
+ /** Run an arbitrary per-item factory. */
511
+ each(factory: StaggerFactory<TItem>, opts?: StaggerOptions): AnimationHandle;
512
+ /** Per-item `animator.tween` with per-item-varying options. */
513
+ tween(opts: StaggerItemTweenOptions<TItem>): AnimationHandle;
514
+ tween<T>(opts: StaggerTargetTweenOptions<T, TItem> & Naming): AnimationHandle;
515
+ tween<T>(opts: StaggerTweenOptions<T, TItem>): AnimationHandle;
516
+ /** Per-item `animator.spring` with per-item-varying options. */
517
+ spring(opts: StaggerItemSpringOptions<TItem>): AnimationHandle;
518
+ spring<T>(opts: StaggerTargetSpringOptions<T, TItem> & Naming): AnimationHandle;
519
+ spring<T>(opts: StaggerSpringOptions<T, TItem>): AnimationHandle;
520
+ }
521
+
395
522
  /** Blends two `T` values at eased progress `t`. Called once per frame; see
396
523
  * {@link InterpolatorFactory} when the blend has setup worth hoisting. */
397
524
  type Interpolate<T> = (from: T, to: T, t: number) => T;
398
- /** Factory interpolator: built ONCE at tween start with (from, to), the returned
399
- * function is called with `t ∈ [0, 1]` each frame. Use for interpolators with
400
- * expensive setup (color-space conversion, path-string parsing) — d3-interpolate's
401
- * shape exactly. For cheap interpolations the per-tick `Interpolate<T>` form is
402
- * fine; this is the escape hatch when setup-per-tick is wasteful. */
525
+ /** Built ONCE at tween start with (from, to); the returned function is called with `t ∈ [0, 1]`
526
+ * each frame. Use for interpolators with expensive setup (color-space conversion, path-string
527
+ * parsing) — d3-interpolate's shape exactly. */
403
528
  type InterpolatorFactory<T> = (from: T, to: T) => (t: number) => T;
404
529
  /** A running animation. Cancel it, or bend its time — pausing and time-scaling
405
530
  * act on this animation's own virtual clock, independent of the animator's. */
@@ -430,13 +555,10 @@ interface TweenOptions<T> {
430
555
  to: T;
431
556
  ms: number;
432
557
  easing?: EasingSpec;
433
- /** Required when T is not `number`. For T = number, defaults to linear numeric lerp.
434
- * Called per-tick with `(from, to, t)`. For interpolators with expensive setup,
435
- * prefer `interpolator` which is built once at tween start. */
558
+ /** Required when T is not `number`. For T = number, defaults to linear numeric lerp. */
436
559
  interpolate?: Interpolate<T>;
437
- /** Factory interpolator built once at tween start. Takes precedence over
438
- * `interpolate` when both are provided. Use this for d3-interpolate or any
439
- * `(from, to) => (t) => v` shape. */
560
+ /** Factory interpolator built once at tween start. Takes precedence over `interpolate`
561
+ * when both are provided. */
440
562
  interpolator?: InterpolatorFactory<T>;
441
563
  onTick: (value: T) => void;
442
564
  onDone?: () => void;
@@ -560,9 +682,8 @@ interface Animator {
560
682
  tween<T>(opts: TweenOptions<T> | TargetTweenOptions<T>): AnimationHandle;
561
683
  spring<T>(opts: SpringOptions<T> | TargetSpringOptions<T>): AnimationHandle;
562
684
  decay<T>(opts: DecayOptions<T> | TargetDecayOptions<T>): AnimationHandle;
563
- /** Unified spring/decay primitive. With `to` set, behaves as a spring;
564
- * with `to: null`, behaves as a velocity-driven decay. Supports
565
- * mid-flight retargeting via the returned handle's `setTarget`. */
685
+ /** With `to` set, behaves as a spring; with `to: null`, as a velocity-driven decay.
686
+ * Retarget mid-flight via the returned handle's `setTarget`. */
566
687
  physics<T>(opts: PhysicsOptions<T> | TargetPhysicsOptions<T>): PhysicsHandle<T>;
567
688
  /** Shows a value on a {@link Target} in place of its committed one until canceled or released.
568
689
  * It covers an animation's move to a destination; a coast, push, or `from` offset adds on top. */
@@ -576,11 +697,9 @@ interface Animator {
576
697
  /** True iff at least one animation is active. With `key`, scoped to that cancelKey. */
577
698
  isActive(key?: string): boolean;
578
699
  /**
579
- * True while the animator is currently executing an animation tick. Useful
580
- * for adapter wrappers (e.g. `animateOnSetPose`) that need to detect
581
- * "this `setPose` was called from inside another animation's onTick"
582
- * (momentum decay, in-flight tween, spring) and avoid recursively
583
- * scheduling a new wrap-animation that would fight the caller.
700
+ * True while the animator is executing an animation tick. Lets an adapter wrapper detect a
701
+ * `setPose` called from inside another animation's onTick and avoid scheduling a new
702
+ * animation that would fight the caller.
584
703
  */
585
704
  isTicking(): boolean;
586
705
  /** Freeze every animation managed by this animator. */
@@ -600,37 +719,26 @@ interface Animator {
600
719
  /** Set per-animation timeScale for every animation whose `cancelKey` matches. */
601
720
  setTimeScaleByKey(key: string, scale: number): void;
602
721
  /**
603
- * Loop primitive: repeatedly invoke `factory` to produce a child animation.
604
- * The factory must wire its returned handle's `onDone` to call `next` so
605
- * the loop advances. Returns a handle whose pause/resume/setTimeScale/cancel
606
- * delegate to the current in-flight child (and prevent future iterations
607
- * on cancel).
608
- *
609
- * The loop is registered with the animator under a supervisor entry so
722
+ * Repeatedly invokes `factory` to produce a child animation. The factory must wire its
723
+ * returned handle's `onDone` to call `next` so the loop advances. The returned handle's
724
+ * pause/resume/setTimeScale/cancel delegate to the in-flight child, and
610
725
  * `animator.cancel(handle)`, `animator.cancelKey(opts.cancelKey)`, and
611
726
  * `animator.isActive(opts.cancelKey)` all work for it.
612
727
  */
613
728
  loop(factory: LoopFactory, opts?: LoopOptions): AnimationHandle;
614
- /** Sugar over `loop` for the common case of looping a tween between two
615
- * values with optional direction handling (`restart` | `reverse` |
616
- * `alternate`). Registered with the animator like `loop`. */
729
+ /** Sugar over `loop`: a tween between two values, repeated per `direction`. Registered
730
+ * with the animator like `loop`. */
617
731
  tweenLoop<T>(opts: TweenLoopOptions<T>): AnimationHandle;
618
732
  /**
619
- * Stagger primitive: schedule a per-item animation, offset by `delay` ms
620
- * per index (or a custom function of the index). Two forms:
621
- * - Factory form: pass `factory` directly, returns a composite
622
- * `AnimationHandle`.
623
- * - Builder form: omit `factory`, get a `StaggerBuilder` for fluent
624
- * `.each` / `.tween` / `.springPose` calls.
733
+ * Schedules a per-item animation, offset by `delay` ms per index (or a custom function of
734
+ * the index). Pass `factory` and get a composite `AnimationHandle`; omit it and get a
735
+ * `StaggerBuilder` for fluent `.each` / `.tween` / `.spring` calls, the last two of which
736
+ * move the items themselves where they are targets.
625
737
  *
626
- * The composite handle's `cancel` cancels the items still waiting AND the
627
- * in-flight children. The delays run on the animator's clock, not the
628
- * wall's: a pause or a time scale, on the handle, by `cancelKey` or on the
629
- * whole animator, holds or stretches the waiting items along with the
630
- * children, and a hidden tab holds both.
631
- *
632
- * The stagger is registered with the animator under a supervisor entry so
633
- * `animator.cancel(handle)`, `animator.cancelKey(opts.cancelKey)`, and
738
+ * The composite handle's `cancel` cancels the items still waiting AND the in-flight
739
+ * children. The delays run on the animator's clock, not the wall's: a pause or a time
740
+ * scale holds or stretches the waiting items along with the children, and a hidden tab
741
+ * holds both. `animator.cancel(handle)`, `animator.cancelKey(opts.cancelKey)`, and
634
742
  * `animator.isActive(opts.cancelKey)` all work for it.
635
743
  */
636
744
  stagger<TItem>(items: readonly TItem[], delay: StaggerDelay): StaggerBuilder<TItem>;
@@ -641,33 +749,21 @@ interface Animator {
641
749
  * pure function of the playhead; event tracks fire only on forward playback.
642
750
  */
643
751
  timeline(opts: TimelineOptions): TimelineHandle;
644
- /** Per-node, per-channel color override registry, painted onto scene nodes
645
- * by `<SceneCanvas animator>` and read by `createPathLayer`. `cycleVertexColors`
646
- * stores an override in it; `tweenVertexColors`, `springVertexColors`, and
647
- * `staggerVertexColors` move its per-node targets, which only this animator
648
- * can move. Cleared automatically on animator unmount. */
752
+ /** Per-node, per-channel color override registry, painted onto scene nodes by
753
+ * `<SceneCanvas animator>` and read by `createPathLayer`. Only this animator can move its
754
+ * per-node targets. Cleared automatically on animator unmount. */
649
755
  colorOverrides: ColorOverrideRegistry;
650
756
  /**
651
- * Subscribe to a callback fired once per RAF frame while any animation is
652
- * active. Returns an unsubscribe function. Used by consumers (typically
653
- * `<SceneCanvas>`) that need to repaint when an animation's side-effect
654
- * is read from a non-scene channel (e.g. `colorOverrides` consulted from
655
- * a custom `drawOne`) — scene mutations naturally trigger a repaint, but
656
- * `colorOverrides` writes do not.
657
- *
658
- * The callback fires AFTER the per-frame tick of each registered
659
- * animation, so by the time it runs `colorOverrides.get(...)` returns
660
- * the latest values. If no animations are active, no tick fires.
757
+ * Subscribe to a callback fired once per RAF frame while any animation is active; returns
758
+ * an unsubscribe. For consumers that repaint from a non-scene channel such as
759
+ * `colorOverrides`, whose writes do not trigger a repaint the way scene mutations do.
760
+ * Fires AFTER the frame's animations have ticked.
661
761
  */
662
762
  onTick(cb: () => void): () => void;
663
763
  /**
664
- * Keep the animator's RAF loop running until the returned cancel
665
- * function is called. Use for animations whose effect is read on every
666
- * frame but which don't have a natural progress state (e.g.
667
- * `cycleVertexColors`, which expresses its current value as a function
668
- * of `performance.now()` rather than as a tween from `from` to `to`).
669
- * Without a keep-alive entry the loop would idle and `onTick` would
670
- * stop firing even though the override is still installed.
764
+ * Keeps the animator's RAF loop running until the returned cancel function is called. For
765
+ * effects read on every frame that have no progress state of their own; without it the
766
+ * loop would idle and `onTick` would stop firing.
671
767
  */
672
768
  keepAlive(): () => void;
673
769
  /**
@@ -765,48 +861,9 @@ interface LoopOptions {
765
861
  /** Names this loop in {@link AnimatorEvent}s and `animator.live()`. */
766
862
  label?: string;
767
863
  }
768
- /** A stagger run's own options: the factory form's fourth argument, and part
769
- * of every fluent builder method's options. */
770
- interface StaggerOptions {
771
- /** Cancel-key for the supervising registration. `animator.cancelKey(key)`
772
- * cancels the whole stagger; `animator.isActive(key)` returns true while
773
- * any timer or child is alive. */
774
- cancelKey?: string;
775
- /** Names this stagger in {@link AnimatorEvent}s and `animator.live()`. */
776
- label?: string;
777
- }
778
864
  /** Produces one iteration of a loop. Must arrange for `next` to be called when
779
865
  * the animation it returns finishes, or the loop stalls after one pass. */
780
866
  type LoopFactory = (iteration: number, next: () => void) => AnimationHandle;
781
- /** Per-index delay schedule. Number ⇒ `index * delay` ms. Function ⇒ caller
782
- * decides the absolute delay for each index (e.g. `i => i * i * 30`). */
783
- type StaggerDelay = number | ((index: number) => number);
784
- /** Produces the animation for one staggered item. */
785
- type StaggerFactory<TItem> = (item: TItem, index: number) => AnimationHandle;
786
- /** A `T` value or a function that derives one from the per-item context. Used
787
- * by the fluent builder methods (`.tween`, `.springPose`) so each item can
788
- * vary an option (e.g. `to: (_item, i) => (i + 1) * 10`). */
789
- type StaggerPerItem<T, TItem> = T | ((item: TItem, index: number) => T);
790
- /** Options for the stagger builder's `.tween`: a tween per item, where
791
- * `from`, `to` and `ms` may each vary by item. `cancelKey` and `label` belong
792
- * to the stagger as a whole, as in {@link StaggerOptions}. */
793
- interface StaggerTweenOptions<T, TItem> extends StaggerOptions {
794
- from: StaggerPerItem<T, TItem>;
795
- to: StaggerPerItem<T, TItem>;
796
- ms: StaggerPerItem<number, TItem>;
797
- easing?: EasingSpec;
798
- interpolate?: Interpolate<T>;
799
- onTick: (value: T, item: TItem, index: number) => void;
800
- onDone?: (item: TItem, index: number) => void;
801
- }
802
- /** Fluent form of `Animator.stagger`: pick what to run per item after the
803
- * items and the delay schedule are already fixed. */
804
- interface StaggerBuilder<TItem> {
805
- /** Run an arbitrary per-item factory. */
806
- each(factory: StaggerFactory<TItem>, opts?: StaggerOptions): AnimationHandle;
807
- /** Sugar: per-item `animator.tween` with per-item-varying options. */
808
- tween<T>(opts: StaggerTweenOptions<T, TItem>): AnimationHandle;
809
- }
810
867
  /** Options for `Animator.tweenLoop` — a tween's options plus how each
811
868
  * iteration relates to the last. */
812
869
  interface TweenLoopOptions<T> {
@@ -831,10 +888,15 @@ interface TweenLoopOptions<T> {
831
888
  * owns, and everything still running is canceled on unmount. */
832
889
  declare function useAnimator(opts?: UseAnimatorOptions): Animator;
833
890
 
891
+ /** The arrays a table can keep its rows' values on screen in. */
892
+ type ShownArray = Float64Array | Float32Array;
834
893
  /** What {@link createTargetTable} takes. */
835
- interface TargetTableOptions {
894
+ interface TargetTableOptions<S extends ShownArray = Float64Array> {
836
895
  /** How many numbers a row holds. */
837
896
  axes: number;
897
+ /** The kind of array to keep `shown` in. Default `Float64Array`. Pass `Float32Array` for a
898
+ * painter that uploads it: the table then fills that each frame, and no copy is made. */
899
+ shown?: new (length: number) => S;
838
900
  /** Rows to make room for at first. The table grows past it. Default 64. */
839
901
  capacity?: number;
840
902
  /** Called after `row` is committed: by `set`, by an animation starting on it, and by one
@@ -849,7 +911,7 @@ interface TargetTableOptions {
849
911
  * is a target (`target(row)`), so it tweens, springs, coasts, and holds like any other, and
850
912
  * every row's value on screen is in `shown` after each frame, with no call per row.
851
913
  */
852
- interface TargetTable {
914
+ interface TargetTable<S extends ShownArray = Float64Array> {
853
915
  readonly axes: number;
854
916
  /** How many rows are in use. */
855
917
  readonly size: number;
@@ -858,8 +920,8 @@ interface TargetTable {
858
920
  readonly committed: Float64Array;
859
921
  /** Every row's value on screen, laid out as `committed`: current after each frame of the
860
922
  * animator moving the rows, and whenever it is read between frames. A new array once the
861
- * table grows. */
862
- readonly shown: Float64Array;
923
+ * table grows. A `Float32Array` where the table was made with `shown: Float32Array`. */
924
+ readonly shown: S;
863
925
  /** Adds a row at `value` and answers its index, which a removed row's index is reused for. */
864
926
  add(value: ArrayLike<number>): number;
865
927
  /** Stops whatever is moving `row` and frees it. Its target is dead from here: a row added
@@ -871,13 +933,15 @@ interface TargetTable {
871
933
  target(row: number): Target<number[]>;
872
934
  }
873
935
  /** Make a {@link TargetTable} whose rows each hold `axes` numbers. */
874
- declare function createTargetTable(opts: TargetTableOptions): TargetTable;
936
+ declare function createTargetTable<S extends ShownArray = Float64Array>(opts: TargetTableOptions<S>): TargetTable<S>;
875
937
 
876
938
  /** How a value maps to the flat numbers blits moves, and back. */
877
939
  interface Axes<T> {
878
940
  count: number;
879
941
  /** Equal for two values exactly when their axes line up one for one. */
880
942
  shape: string;
943
+ /** A name for each axis where the value has them: a plain object's fields. */
944
+ names?: readonly string[];
881
945
  to(v: T): number[];
882
946
  /** The value whose `count` axes start at `at` in `a`. */
883
947
  from(a: ArrayLike<number>, at?: number): T;
@@ -1066,6 +1130,8 @@ type ClipValue = number | string | boolean | null | ClipValue[] | {
1066
1130
  /** A keyframe as data. Its easing is a built-in's name or bezier control points. */
1067
1131
  interface ClipKey {
1068
1132
  t: number;
1133
+ /** A typed array in the live key's value, as a `Path` has, is held as a `TaggedTypedArray`
1134
+ * from `@weasel-js/geom`: `{ __ta: 'f32', data: [...] }`. `bindClip` makes it the array again. */
1069
1135
  value: ClipValue;
1070
1136
  easing?: EasingName | BezierEasing;
1071
1137
  }
@@ -1079,8 +1145,12 @@ interface ClipSampledTrack {
1079
1145
  /** Resolved from `ClipBinder.interpolate`. Without one the track is sampled in its
1080
1146
  * target's axes, or field by field. */
1081
1147
  interpolate?: string;
1082
- weight?: number;
1083
- axes?: number[];
1148
+ /** The track's share of its target: a number, or a name for `ClipBinder.weight` to resolve
1149
+ * into a function read each frame, as a crossfade's is. */
1150
+ weight?: number | string;
1151
+ /** The part of the target the track holds: the target's names for its axes, a rig's joints
1152
+ * among them, or positions in its axes where it has no names. */
1153
+ axes?: number[] | string[];
1084
1154
  }
1085
1155
  /** An `EventTrack` as data: each event is a name and what to carry with it. */
1086
1156
  interface ClipEventTrack {
@@ -1117,6 +1187,8 @@ interface ClipBinder {
1117
1187
  target?(name: string): Target<any> | undefined;
1118
1188
  /** Interpolations by name. */
1119
1189
  interpolate?: Readonly<Record<string, Interpolate<any>>>;
1190
+ /** The function a track's named weight stands for. */
1191
+ weight?(name: string): (() => number) | undefined;
1120
1192
  /** What an event does: its `fire`, its `book`, or both. */
1121
1193
  event?(name: string, data: ClipValue | undefined): Pick<TimelineEvent, 'fire' | 'book'> | undefined;
1122
1194
  }
@@ -1131,8 +1203,9 @@ declare function bindClip(clip: Clip, binder: ClipBinder): NestedTimeline;
1131
1203
  * A live timeline as a clip: `{ tracks, duration }` as `animator.timeline` takes them, or
1132
1204
  * `{ tracks: handle.tracks() }` off a running one. Each track is written by the names it
1133
1205
  * carries (`targetName`, `interpolateName`, an event's `name`), which `bindClip` sets and a
1134
- * track written by hand can set too. Throws, naming the track, on anything a clip cannot
1135
- * hold: a function with no name, an `onTick`, or a value JSON would not read back.
1206
+ * track written by hand can set too. A typed array in a key's value is written tagged.
1207
+ * Throws, naming the track, on anything a clip cannot hold: a function with no name, an
1208
+ * `onTick`, or a value JSON would not read back.
1136
1209
  */
1137
1210
  declare function toClip(timeline: NestedTimeline): Clip;
1138
1211
 
@@ -1167,17 +1240,11 @@ declare const IDENTITY_JOINT: JointTransform;
1167
1240
 
1168
1241
  /**
1169
1242
  * `pose` reflected across the rig's y axis, as deltas against the same
1170
- * skeleton — so the mirrored pose still blends, samples and resolves like any
1171
- * other.
1172
- *
1173
- * Reflecting a joint chain is conjugation: every local transform `T·R·S`
1174
- * becomes `F·T·R·S·F` with `F` the flip, which is again a plain transform
1175
- * with `x` and `rotation` negated. Every resolved joint is then exactly the
1176
- * original's reflection, which is how a rig faces the other way without a
1177
- * negative scale anywhere — a scene pose such as `RectPose` has no term to
1178
- * carry one. The negation applies to bind plus delta, so the delta for a
1179
- * joint the pose leaves at its bind is nonzero whenever the bind is.
1243
+ * skeleton — so the mirrored pose still blends, samples, and resolves like any
1244
+ * other, and a rig faces the other way without a negative scale anywhere.
1180
1245
  *
1246
+ * Each joint's `x` and `rotation` are negated as bind plus delta, so the delta
1247
+ * for a joint the pose leaves at its bind is nonzero whenever the bind is.
1181
1248
  * Applying it twice gives the original pose back.
1182
1249
  */
1183
1250
  declare function mirrorPose(skeleton: Skeleton, pose: Pose): Pose;
@@ -1194,14 +1261,16 @@ declare function mirrorPose(skeleton: Skeleton, pose: Pose): Pose;
1194
1261
  declare function resolveSkeleton(skeleton: Skeleton, pose: Pose): Map<string, GlMat3>;
1195
1262
 
1196
1263
  /**
1197
- * The axes of `skeletonAxes(skeleton)` that belong to `joints`, for a hold or a track over part
1198
- * of a rig: `animator.hold({ target: rig.target, value: pose, axes: jointAxes(skeleton, arm) })`.
1264
+ * The axes of `skeletonAxes(skeleton)` that belong to `joints`, as indexes, for a caller with
1265
+ * a skeleton and no target. A hold or a track on a rig's target takes the joints' names
1266
+ * themselves: `animator.hold({ target: rig.target, value: pose, axes: arm })`.
1199
1267
  */
1200
1268
  declare function jointAxes(skeleton: Skeleton, joints: readonly string[]): number[];
1201
1269
  /**
1202
1270
  * How a {@link Pose} of `skeleton` maps to the numbers an animation moves: five per joint in
1203
1271
  * skeleton order (`x`, `y`, `rotation`, `scaleX`, `scaleY`), every rotation an angle, so a pose
1204
- * blends the short way round. A joint or field the pose leaves out reads as its identity, and a
1272
+ * blends the short way round. Each axis is named `<joint>.<field>`, so `axes: ['knee']` on a hold
1273
+ * reaches a joint's five and `['knee.rotation']` one of them. A joint or field the pose leaves out reads as its identity, and a
1205
1274
  * pose read back names every joint in full. Pass it as `axes` to `createTarget` for a pose target
1206
1275
  * of your own; a bound rig's is `Rig.target`.
1207
1276
  */
@@ -1254,23 +1323,16 @@ interface IkResult {
1254
1323
  }
1255
1324
  /**
1256
1325
  * Rotate a joint chain so its tip reaches `target`, returning the result as an
1257
- * ordinary {@link Pose} — so it keys a `SampledTrack<Pose>`, drives a rig through
1258
- * `Rig.pose`, and is held on a rig like any other. Weighting the IK against an
1259
- * animated pose is `animator.hold({ target: rig.target, value: solved, weight: w })`
1260
- * beside the clip at weight `1 - w`; with `axes: jointAxes(skeleton, joints)` on both, the
1326
+ * ordinary {@link Pose}. Weighting the IK against an animated pose is
1327
+ * `animator.hold({ target: rig.target, value: solved, weight: w })` beside the
1328
+ * clip at weight `1 - w`; with `axes: jointAxes(skeleton, joints)` on both, the
1261
1329
  * rest of the body is left to whatever else holds it.
1262
1330
  *
1263
- * The solver is cyclic coordinate descent: each step turns one joint so the
1264
- * tip lies on the ray from that joint to the target. A joint's rotation is the
1265
- * one term the rig lets it change, and a CCD step is exactly a change to it, so
1266
- * limits clamp the value the pose stores and nothing has to be converted back
1267
- * from positions. Each step is solved in the joint's parent frame, which makes
1268
- * it exact under any parent transform the skeleton composes, mirrors included.
1269
- *
1270
- * A two-joint chain is solved analytically first: of the two bends that reach
1271
- * the target it takes the one on `pole`'s side, or the input pose's side, and
1272
- * falls back to the other bend and then to CCD when limits refuse it. A target
1273
- * beyond the chain's reach straightens the chain toward it.
1331
+ * The solver is cyclic coordinate descent, and limits clamp the rotation the
1332
+ * pose stores. A two-joint chain is solved analytically first: of the two bends
1333
+ * that reach the target it takes the one on `pole`'s side, or the input pose's
1334
+ * side, and falls back to the other bend and then to CCD when limits refuse it.
1335
+ * A target beyond the chain's reach straightens the chain toward it.
1274
1336
  */
1275
1337
  declare function solveIk(skeleton: Skeleton, pose: Pose, options: SolveIkOptions): IkResult;
1276
1338
 
@@ -1350,4 +1412,4 @@ declare function setWeights(skin: Skin, count: number, joint: string, weights: R
1350
1412
  */
1351
1413
  declare function brushWeights(coords: ArrayLike<number>, x: number, y: number, radius: number, current: readonly number[] | undefined, mode: 'add' | 'remove'): Map<number, number>;
1352
1414
 
1353
- export { type AnimationHandle, type AnimationInfo, type AnimationKind, type Animator, type AnimatorEvent, type AnimatorListener, type AutoSkinOptions, type Axes, type AxisBuffer, type Clip, type ClipBinder, type ClipEventTrack, type ClipKey, type ClipSampledTrack, type ClipTimeline, type ClipTimelineTrack, type ClipTrack, type ClipValue, type ColorInterpolate, type ColorOverride, type ColorOverrideFn, ColorOverrideRegistry, type CycleHandle, type CycleVertexColorsOptions, type DecayOptions, type EventBooking, type EventBookingHandle, type EventTrack, type HoldHandle, type HoldOptions, IDENTITY_JOINT, type IkLimit, type IkPoint, type IkResult, type Interpolate, type InterpolatorFactory, type Joint, type JointTransform, type Keyframe, type LiveAnimation, type LoopFactory, type LoopOptions, type NestedTimeline, type PhysicsHandle, type PhysicsOptions, type Pose, type SampledTrack, type Skeleton, type Skin, type Slew, type SlewRates, type SolveIkOptions, type SpringOptions, type SpringVertexColorsOptions, type StaggerBuilder, type StaggerDelay, type StaggerFactory, type StaggerOptions, type StaggerPerItem, type StaggerTweenOptions, type StaggerVertexColorsOptions, type Target, type TargetAxes, type TargetDecayOptions, type TargetOptions, type TargetPhysicsOptions, type TargetSpringOptions, type TargetTable, type TargetTableOptions, type TargetTweenOptions, type TimelineClock, type TimelineEvent, type TimelineHandle, type TimelineOptions, type TimelineTrack, type Track, type TweenLoopOptions, type TweenOptions, type TweenVertexColorsOptions, type UseAnimatorOptions, type VertexColorChannel, addJoint, autoSkin, axesOf, bindClip, brushWeights, createSlew, createTarget, createTargetTable, cycleVertexColors, dropSkinJoints, jointAt, jointAxes, jointPositions, mirrorPose, motionsFromRest, moveJoint, rainbowVertexColors, removeJoint, resolveSkeleton, sampleTrack, setWeights, skeletonAxes, skinPath, skinnable, slewToward, solidVertexColors, solveIk, springVertexColors, staggerVertexColors, toClip, tweenVertexColors, useAnimator };
1415
+ export { type AnimationHandle, type AnimationInfo, type AnimationKind, type Animator, type AnimatorEvent, type AnimatorListener, type AutoSkinOptions, type Axes, type AxisBuffer, type AxisName, type AxisRef, type Clip, type ClipBinder, type ClipEventTrack, type ClipKey, type ClipSampledTrack, type ClipTimeline, type ClipTimelineTrack, type ClipTrack, type ClipValue, type ColorInterpolate, type ColorOverride, type ColorOverrideFn, ColorOverrideRegistry, type CycleHandle, type CycleVertexColorsOptions, type DecayOptions, type EventBooking, type EventBookingHandle, type EventTrack, type HoldHandle, type HoldOptions, IDENTITY_JOINT, type IkLimit, type IkPoint, type IkResult, type Interpolate, type InterpolatorFactory, type Joint, type JointTransform, type Keyframe, type LiveAnimation, type LoopFactory, type LoopOptions, type Naming, type NestedTimeline, type PhysicsHandle, type PhysicsOptions, type Pose, type SampledTrack, type ShownArray, type Skeleton, type Skin, type Slew, type SlewRates, type SolveIkOptions, type SpringOptions, type SpringVertexColorsOptions, type StaggerBuilder, type StaggerDelay, type StaggerFactory, type StaggerItemSpringOptions, type StaggerItemTweenOptions, type StaggerOptions, type StaggerPerItem, type StaggerSpringOptions, type StaggerTargetSpringOptions, type StaggerTargetTweenOptions, type StaggerTweenOptions, type StaggerVertexColorsOptions, type Target, type TargetAxes, type TargetDecayOptions, type TargetOptions, type TargetPhysicsOptions, type TargetSpringOptions, type TargetTable, type TargetTableOptions, type TargetTweenOptions, type TimelineClock, type TimelineEvent, type TimelineHandle, type TimelineOptions, type TimelineTrack, type Track, type TweenLoopOptions, type TweenOptions, type TweenVertexColorsOptions, type UseAnimatorOptions, type VertexColorChannel, addJoint, autoSkin, axesOf, bindClip, brushWeights, createSlew, createTarget, createTargetTable, cycleVertexColors, dropSkinJoints, jointAt, jointAxes, jointPositions, mirrorPose, motionsFromRest, moveJoint, rainbowVertexColors, removeJoint, resolveSkeleton, sampleTrack, setWeights, skeletonAxes, skinPath, skinnable, slewToward, solidVertexColors, solveIk, springVertexColors, staggerVertexColors, toClip, tweenVertexColors, useAnimator };