@voila.dev/motion 6.0.124 → 6.0.126

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.
@@ -1 +1 @@
1
- {"version":3,"file":"sequence.d.ts","sourceRoot":"","sources":["../../src/sequence/sequence.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,SAAS,EACT,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,IAAI,MAAM,EAAE,GAAG,IAAI,MAAM,EAAE,GAAG,GAAG,GAAG,MAAM,CAAC;AAE7E,MAAM,WAAW,cAAe,SAAQ,gBAAgB;IACvD,QAAQ,CAAC,EAAE,CAAC,EAAE,UAAU,CAAC;CACzB;AAED,MAAM,MAAM,OAAO,GAChB,SAAS,CAAC,aAAa,EAAE,YAAY,EAAE,cAAc,CAAC,CAAC,GAEvD,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,cAAc,CAAC,CAAC,GAC5D,MAAM,GACN;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,UAAU,CAAA;CAAE,CAAC;AAEvD,MAAM,MAAM,UAAU,GAAG,CACxB,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,OAAO,EAClB,OAAO,EAAE,gBAAgB,KACrB,iBAAiB,CAAC;AAEvB,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,OAAO,KAAK,SAAS,MAAM,EAAE,CAAC;AA6CpE;;;;;GAKG;AACH,wBAAgB,eAAe,CAC9B,QAAQ,EAAE,SAAS,OAAO,EAAE,EAC5B,OAAO,EAAE,gBAAgB,EACzB,UAAU,EAAE,UAAU,EACtB,cAAc,EAAE,cAAc,GAC5B,iBAAiB,CA4GnB"}
1
+ {"version":3,"file":"sequence.d.ts","sourceRoot":"","sources":["../../src/sequence/sequence.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,SAAS,EACT,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,IAAI,MAAM,EAAE,GAAG,IAAI,MAAM,EAAE,GAAG,GAAG,GAAG,MAAM,CAAC;AAE7E,MAAM,WAAW,cAAe,SAAQ,gBAAgB;IACvD,QAAQ,CAAC,EAAE,CAAC,EAAE,UAAU,CAAC;CACzB;AAED,MAAM,MAAM,OAAO,GAChB,SAAS,CAAC,aAAa,EAAE,YAAY,EAAE,cAAc,CAAC,CAAC,GAEvD,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,cAAc,CAAC,CAAC,GAC5D,MAAM,GACN;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,UAAU,CAAA;CAAE,CAAC;AAEvD,MAAM,MAAM,UAAU,GAAG,CACxB,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,OAAO,EAClB,OAAO,EAAE,gBAAgB,KACrB,iBAAiB,CAAC;AAEvB,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,OAAO,KAAK,SAAS,MAAM,EAAE,CAAC;AA6CpE;;;;;GAKG;AACH,wBAAgB,eAAe,CAC9B,QAAQ,EAAE,SAAS,OAAO,EAAE,EAC5B,OAAO,EAAE,gBAAgB,EACzB,UAAU,EAAE,UAAU,EACtB,cAAc,EAAE,cAAc,GAC5B,iBAAiB,CAuKnB"}
@@ -27,13 +27,21 @@ function animateSequence(segments, options, animateOne, resolveTargets) {
27
27
  let rate = 1;
28
28
  let base = 0;
29
29
  let since = now();
30
+ let paused = false;
31
+ let halted = false;
32
+ let completing = false;
30
33
  function elapsed() {
31
- return base + (now() - since) / 1e3 * rate;
34
+ return paused ? base : base + (now() - since) / 1e3 * rate;
35
+ }
36
+ function retime(seconds) {
37
+ base = seconds;
38
+ since = now();
32
39
  }
33
40
  const labels = /* @__PURE__ */ new Map();
34
41
  const busy = /* @__PURE__ */ new Map();
35
42
  const children = [];
36
- const late = [];
43
+ const origins = /* @__PURE__ */ new Map();
44
+ const openers = [];
37
45
  const waits = [];
38
46
  let end = 0;
39
47
  let cursor = 0;
@@ -74,13 +82,26 @@ function animateSequence(segments, options, animateOne, resolveTargets) {
74
82
  finished2 = controls.finished;
75
83
  ends = Math.max(start, controls.duration);
76
84
  } else {
85
+ let open2 = function() {
86
+ if (opened || halted || rate < 0) return opened?.finished;
87
+ const at = elapsed();
88
+ opened = launch2();
89
+ const origin = Math.min(at, begins);
90
+ origins.set(opened, origin);
91
+ opened.time = at - origin;
92
+ if (paused) opened.pause();
93
+ if (completing) opened.complete();
94
+ children.push(opened);
95
+ return opened.finished;
96
+ };
97
+ var open = open2;
98
+ const begins = start + (typeof delay === "number" ? delay : 0);
99
+ let opened;
100
+ openers.push((seconds) => {
101
+ if (seconds >= begins) open2();
102
+ });
77
103
  finished2 = Promise.all(before.map((earlier) => earlier.finished)).then(
78
- () => {
79
- if (rate < 0) return;
80
- const controls = launch2();
81
- late.push(controls);
82
- return controls.finished;
83
- }
104
+ open2
84
105
  );
85
106
  waits.push(finished2);
86
107
  ends = start + plannedDuration(keyframes, { ...options, ...own });
@@ -98,16 +119,51 @@ function animateSequence(segments, options, animateOne, resolveTargets) {
98
119
  const finished = Promise.all([group.finished, ...waits]).then(
99
120
  () => void 0
100
121
  );
122
+ function around(action, first) {
123
+ const own = group[action];
124
+ return {
125
+ value() {
126
+ first();
127
+ own();
128
+ }
129
+ };
130
+ }
101
131
  Object.defineProperties(group, {
132
+ time: {
133
+ get: elapsed,
134
+ set(seconds) {
135
+ retime(seconds);
136
+ for (const child of children) {
137
+ child.time = seconds - (origins.get(child) ?? 0);
138
+ }
139
+ for (const opener of openers) opener(seconds);
140
+ }
141
+ },
142
+ pause: around("pause", () => {
143
+ retime(elapsed());
144
+ paused = true;
145
+ }),
146
+ play: around("play", () => {
147
+ retime(elapsed());
148
+ paused = false;
149
+ }),
150
+ stop: around("stop", () => {
151
+ halted = true;
152
+ }),
153
+ cancel: around("cancel", () => {
154
+ halted = true;
155
+ }),
156
+ complete: around("complete", () => {
157
+ completing = true;
158
+ }),
102
159
  // The segments still waiting count too: they end after the ones launched.
103
160
  duration: { value: Math.max(end, group.duration) },
104
161
  speed: {
105
162
  get: () => rate,
106
163
  set(next) {
107
- base = elapsed();
108
- since = now();
164
+ retime(elapsed());
109
165
  rate = next;
110
- for (const child of [...children, ...late]) child.speed = next;
166
+ for (const child of children) child.speed = next;
111
167
  }
112
168
  }
113
169
  });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/sequence/sequence.ts"],
4
- "sourcesContent": ["import { groupControls, thenable } from \"#/animate/controls.ts\";\nimport { planGenerator, totalDuration } from \"#/animate/plan.ts\";\nimport type {\n\tAnimationControls,\n\tAnimationOptions,\n\tDOMKeyframes,\n\tElementTarget,\n\tKeyframes,\n} from \"#/animate/types.ts\";\nimport { now } from \"#/frame/frame.ts\";\nimport type { MotionValue } from \"#/value/motion-value.ts\";\n\n/**\n * When a segment starts: seconds from the start of the sequence, `\"+0.1\"` or\n * `\"-0.2\"` from the end of the segment before, `\"<\"` with the segment before,\n * or the name of a label.\n */\nexport type SequenceAt = number | `+${number}` | `-${number}` | \"<\" | string;\n\nexport interface SegmentOptions extends AnimationOptions {\n\treadonly at?: SequenceAt;\n}\n\nexport type Segment =\n\t| readonly [ElementTarget, DOMKeyframes, SegmentOptions?]\n\t// biome-ignore lint/suspicious/noExplicitAny: a sequence mixes values of any type.\n\t| readonly [MotionValue<any>, Keyframes<any>, SegmentOptions?]\n\t| string\n\t| { readonly name: string; readonly at?: SequenceAt };\n\nexport type AnimateOne = (\n\ttarget: unknown,\n\tkeyframes: unknown,\n\toptions: AnimationOptions,\n) => AnimationControls;\n\nexport type ResolveTargets = (target: unknown) => readonly object[];\n\nfunction startOf(\n\tat: SequenceAt | undefined,\n\tcursor: number,\n\tpreviousStart: number,\n\tlabels: ReadonlyMap<string, number>,\n): number {\n\tif (at === undefined) return cursor;\n\tif (typeof at === \"number\") return at;\n\tif (at === \"<\") return previousStart;\n\tif (/^[+-]/.test(at)) return Math.max(0, cursor + Number(at));\n\treturn labels.get(at) ?? cursor;\n}\n\n/**\n * How long a segment that waits for an earlier one will run once launched.\n * Its first keyframe is only read then, but a tween's length doesn't depend\n * on it, and neither does a default spring's (its rest thresholds scale with\n * the distance).\n */\nfunction plannedDuration(keyframes: unknown, options: AnimationOptions) {\n\tconst channels =\n\t\ttypeof keyframes === \"object\" &&\n\t\tkeyframes !== null &&\n\t\t!Array.isArray(keyframes)\n\t\t\t? Object.values(keyframes)\n\t\t\t: [keyframes];\n\tconst { delay } = options;\n\treturn (\n\t\t(typeof delay === \"number\" ? delay : 0) +\n\t\tMath.max(\n\t\t\t0,\n\t\t\t...channels.map((channel) => {\n\t\t\t\tconst frames = Array.isArray(channel) ? channel : [0, channel];\n\t\t\t\tconst { generator } = planGenerator(\n\t\t\t\t\tframes.map((_frame, index) => index),\n\t\t\t\t\toptions,\n\t\t\t\t);\n\t\t\t\treturn totalDuration(generator.duration, options);\n\t\t\t}),\n\t\t)\n\t);\n}\n\n/**\n * Launches every segment at once, each delayed to its place on the\n * timeline, so the whole sequence shares one clock and can be scrubbed. A\n * segment that animates a property an earlier segment already animates\n * waits for that one to finish, then starts from where it left off.\n */\nexport function animateSequence(\n\tsegments: readonly Segment[],\n\toptions: AnimationOptions,\n\tanimateOne: AnimateOne,\n\tresolveTargets: ResolveTargets,\n): AnimationControls {\n\t/** The sequence's own clock: it runs at the speed the sequence is set to. */\n\tlet rate = 1;\n\tlet base = 0;\n\tlet since = now();\n\tfunction elapsed(): number {\n\t\treturn base + ((now() - since) / 1000) * rate;\n\t}\n\tconst labels = new Map<string, number>();\n\tconst busy = new Map<\n\t\tobject,\n\t\tMap<string, { readonly finished: Promise<void>; readonly end: number }>\n\t>();\n\tconst children: AnimationControls[] = [];\n\t/** The segments that waited, once launched: they take later speed changes too. */\n\tconst late: AnimationControls[] = [];\n\tconst waits: Promise<void>[] = [];\n\tlet end = 0;\n\tlet cursor = 0;\n\tlet previousStart = 0;\n\tfor (const segment of segments) {\n\t\tif (typeof segment === \"string\" || \"name\" in segment) {\n\t\t\tconst label = typeof segment === \"string\" ? { name: segment } : segment;\n\t\t\tlabels.set(label.name, startOf(label.at, cursor, previousStart, labels));\n\t\t\tcontinue;\n\t\t}\n\t\tconst [target, keyframes, own = {}] = segment as readonly [\n\t\t\tunknown,\n\t\t\tunknown,\n\t\t\tSegmentOptions?,\n\t\t];\n\t\tconst placed = startOf(own.at, cursor, previousStart, labels);\n\t\tconst keys =\n\t\t\ttypeof keyframes === \"object\" &&\n\t\t\tkeyframes !== null &&\n\t\t\t!Array.isArray(keyframes)\n\t\t\t\t? Object.keys(keyframes)\n\t\t\t\t: [\"value\"];\n\t\tconst targets = resolveTargets(target);\n\t\tconst before = targets.flatMap((one) =>\n\t\t\tkeys.flatMap((key) => busy.get(one)?.get(key) ?? []),\n\t\t);\n\t\tconst start = Math.max(placed, ...before.map((earlier) => earlier.end));\n\t\tconst { delay } = own;\n\t\t/** Launched late, a segment only waits for what is left of its delay. */\n\t\tfunction optionsAt(elapsed: number): AnimationOptions {\n\t\t\tconst offset = start - elapsed;\n\t\t\treturn {\n\t\t\t\t...options,\n\t\t\t\t...own,\n\t\t\t\tdelay:\n\t\t\t\t\ttypeof delay === \"function\"\n\t\t\t\t\t\t? (index, total) => Math.max(0, offset + delay(index, total))\n\t\t\t\t\t\t: Math.max(0, offset + (delay ?? 0)),\n\t\t\t};\n\t\t}\n\t\tfunction launch(): AnimationControls {\n\t\t\tconst controls = animateOne(target, keyframes, optionsAt(elapsed()));\n\t\t\tcontrols.speed = rate;\n\t\t\treturn controls;\n\t\t}\n\t\tlet finished: Promise<void>;\n\t\tlet ends: number;\n\t\tif (before.length === 0) {\n\t\t\tconst controls = launch();\n\t\t\tchildren.push(controls);\n\t\t\tfinished = controls.finished;\n\t\t\tends = Math.max(start, controls.duration);\n\t\t} else {\n\t\t\t// Played backwards, the sequence went back past it: it never starts.\n\t\t\tfinished = Promise.all(before.map((earlier) => earlier.finished)).then(\n\t\t\t\t() => {\n\t\t\t\t\tif (rate < 0) return;\n\t\t\t\t\tconst controls = launch();\n\t\t\t\t\tlate.push(controls);\n\t\t\t\t\treturn controls.finished;\n\t\t\t\t},\n\t\t\t);\n\t\t\twaits.push(finished);\n\t\t\tends = start + plannedDuration(keyframes, { ...options, ...own });\n\t\t}\n\t\tcursor = ends;\n\t\tend = Math.max(end, ends);\n\t\tfor (const one of targets) {\n\t\t\tconst byKey = busy.get(one) ?? new Map();\n\t\t\tbusy.set(one, byKey);\n\t\t\tfor (const key of keys) byKey.set(key, { finished, end: ends });\n\t\t}\n\t\tpreviousStart = start;\n\t}\n\tconst group = groupControls(children);\n\tconst finished = Promise.all([group.finished, ...waits]).then(\n\t\t() => undefined,\n\t);\n\tObject.defineProperties(group, {\n\t\t// The segments still waiting count too: they end after the ones launched.\n\t\tduration: { value: Math.max(end, group.duration) },\n\t\tspeed: {\n\t\t\tget: () => rate,\n\t\t\tset(next: number) {\n\t\t\t\tbase = elapsed();\n\t\t\t\tsince = now();\n\t\t\t\trate = next;\n\t\t\t\tfor (const child of [...children, ...late]) child.speed = next;\n\t\t\t},\n\t\t},\n\t});\n\treturn Object.assign(group, thenable(finished));\n}\n"],
5
- "mappings": "AAAA,SAAS,eAAe,gBAAgB;AACxC,SAAS,eAAe,qBAAqB;AAQ7C,SAAS,WAAW;AA6BpB,SAAS,QACR,IACA,QACA,eACA,QACS;AACT,MAAI,OAAO,OAAW,QAAO;AAC7B,MAAI,OAAO,OAAO,SAAU,QAAO;AACnC,MAAI,OAAO,IAAK,QAAO;AACvB,MAAI,QAAQ,KAAK,EAAE,EAAG,QAAO,KAAK,IAAI,GAAG,SAAS,OAAO,EAAE,CAAC;AAC5D,SAAO,OAAO,IAAI,EAAE,KAAK;AAC1B;AAQA,SAAS,gBAAgB,WAAoB,SAA2B;AACvE,QAAM,WACL,OAAO,cAAc,YACrB,cAAc,QACd,CAAC,MAAM,QAAQ,SAAS,IACrB,OAAO,OAAO,SAAS,IACvB,CAAC,SAAS;AACd,QAAM,EAAE,MAAM,IAAI;AAClB,UACE,OAAO,UAAU,WAAW,QAAQ,KACrC,KAAK;AAAA,IACJ;AAAA,IACA,GAAG,SAAS,IAAI,CAAC,YAAY;AAC5B,YAAM,SAAS,MAAM,QAAQ,OAAO,IAAI,UAAU,CAAC,GAAG,OAAO;AAC7D,YAAM,EAAE,UAAU,IAAI;AAAA,QACrB,OAAO,IAAI,CAAC,QAAQ,UAAU,KAAK;AAAA,QACnC;AAAA,MACD;AACA,aAAO,cAAc,UAAU,UAAU,OAAO;AAAA,IACjD,CAAC;AAAA,EACF;AAEF;AAQO,SAAS,gBACf,UACA,SACA,YACA,gBACoB;AAEpB,MAAI,OAAO;AACX,MAAI,OAAO;AACX,MAAI,QAAQ,IAAI;AAChB,WAAS,UAAkB;AAC1B,WAAO,QAAS,IAAI,IAAI,SAAS,MAAQ;AAAA,EAC1C;AACA,QAAM,SAAS,oBAAI,IAAoB;AACvC,QAAM,OAAO,oBAAI,IAGf;AACF,QAAM,WAAgC,CAAC;AAEvC,QAAM,OAA4B,CAAC;AACnC,QAAM,QAAyB,CAAC;AAChC,MAAI,MAAM;AACV,MAAI,SAAS;AACb,MAAI,gBAAgB;AACpB,aAAW,WAAW,UAAU;AAyB/B,QAASA,aAAT,SAAmBC,UAAmC;AACrD,YAAM,SAAS,QAAQA;AACvB,aAAO;AAAA,QACN,GAAG;AAAA,QACH,GAAG;AAAA,QACH,OACC,OAAO,UAAU,aACd,CAAC,OAAO,UAAU,KAAK,IAAI,GAAG,SAAS,MAAM,OAAO,KAAK,CAAC,IAC1D,KAAK,IAAI,GAAG,UAAU,SAAS,EAAE;AAAA,MACtC;AAAA,IACD,GACSC,UAAT,WAAqC;AACpC,YAAM,WAAW,WAAW,QAAQ,WAAWF,WAAU,QAAQ,CAAC,CAAC;AACnE,eAAS,QAAQ;AACjB,aAAO;AAAA,IACR;AAfS,oBAAAA,YAWA,SAAAE;AAnCT,QAAI,OAAO,YAAY,YAAY,UAAU,SAAS;AACrD,YAAM,QAAQ,OAAO,YAAY,WAAW,EAAE,MAAM,QAAQ,IAAI;AAChE,aAAO,IAAI,MAAM,MAAM,QAAQ,MAAM,IAAI,QAAQ,eAAe,MAAM,CAAC;AACvE;AAAA,IACD;AACA,UAAM,CAAC,QAAQ,WAAW,MAAM,CAAC,CAAC,IAAI;AAKtC,UAAM,SAAS,QAAQ,IAAI,IAAI,QAAQ,eAAe,MAAM;AAC5D,UAAM,OACL,OAAO,cAAc,YACrB,cAAc,QACd,CAAC,MAAM,QAAQ,SAAS,IACrB,OAAO,KAAK,SAAS,IACrB,CAAC,OAAO;AACZ,UAAM,UAAU,eAAe,MAAM;AACrC,UAAM,SAAS,QAAQ;AAAA,MAAQ,CAAC,QAC/B,KAAK,QAAQ,CAAC,QAAQ,KAAK,IAAI,GAAG,GAAG,IAAI,GAAG,KAAK,CAAC,CAAC;AAAA,IACpD;AACA,UAAM,QAAQ,KAAK,IAAI,QAAQ,GAAG,OAAO,IAAI,CAAC,YAAY,QAAQ,GAAG,CAAC;AACtE,UAAM,EAAE,MAAM,IAAI;AAkBlB,QAAIC;AACJ,QAAI;AACJ,QAAI,OAAO,WAAW,GAAG;AACxB,YAAM,WAAWD,QAAO;AACxB,eAAS,KAAK,QAAQ;AACtB,MAAAC,YAAW,SAAS;AACpB,aAAO,KAAK,IAAI,OAAO,SAAS,QAAQ;AAAA,IACzC,OAAO;AAEN,MAAAA,YAAW,QAAQ,IAAI,OAAO,IAAI,CAAC,YAAY,QAAQ,QAAQ,CAAC,EAAE;AAAA,QACjE,MAAM;AACL,cAAI,OAAO,EAAG;AACd,gBAAM,WAAWD,QAAO;AACxB,eAAK,KAAK,QAAQ;AAClB,iBAAO,SAAS;AAAA,QACjB;AAAA,MACD;AACA,YAAM,KAAKC,SAAQ;AACnB,aAAO,QAAQ,gBAAgB,WAAW,EAAE,GAAG,SAAS,GAAG,IAAI,CAAC;AAAA,IACjE;AACA,aAAS;AACT,UAAM,KAAK,IAAI,KAAK,IAAI;AACxB,eAAW,OAAO,SAAS;AAC1B,YAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,oBAAI,IAAI;AACvC,WAAK,IAAI,KAAK,KAAK;AACnB,iBAAW,OAAO,KAAM,OAAM,IAAI,KAAK,EAAE,UAAAA,WAAU,KAAK,KAAK,CAAC;AAAA,IAC/D;AACA,oBAAgB;AAAA,EACjB;AACA,QAAM,QAAQ,cAAc,QAAQ;AACpC,QAAM,WAAW,QAAQ,IAAI,CAAC,MAAM,UAAU,GAAG,KAAK,CAAC,EAAE;AAAA,IACxD,MAAM;AAAA,EACP;AACA,SAAO,iBAAiB,OAAO;AAAA;AAAA,IAE9B,UAAU,EAAE,OAAO,KAAK,IAAI,KAAK,MAAM,QAAQ,EAAE;AAAA,IACjD,OAAO;AAAA,MACN,KAAK,MAAM;AAAA,MACX,IAAI,MAAc;AACjB,eAAO,QAAQ;AACf,gBAAQ,IAAI;AACZ,eAAO;AACP,mBAAW,SAAS,CAAC,GAAG,UAAU,GAAG,IAAI,EAAG,OAAM,QAAQ;AAAA,MAC3D;AAAA,IACD;AAAA,EACD,CAAC;AACD,SAAO,OAAO,OAAO,OAAO,SAAS,QAAQ,CAAC;AAC/C;",
6
- "names": ["optionsAt", "elapsed", "launch", "finished"]
4
+ "sourcesContent": ["import { groupControls, thenable } from \"#/animate/controls.ts\";\nimport { planGenerator, totalDuration } from \"#/animate/plan.ts\";\nimport type {\n\tAnimationControls,\n\tAnimationOptions,\n\tDOMKeyframes,\n\tElementTarget,\n\tKeyframes,\n} from \"#/animate/types.ts\";\nimport { now } from \"#/frame/frame.ts\";\nimport type { MotionValue } from \"#/value/motion-value.ts\";\n\n/**\n * When a segment starts: seconds from the start of the sequence, `\"+0.1\"` or\n * `\"-0.2\"` from the end of the segment before, `\"<\"` with the segment before,\n * or the name of a label.\n */\nexport type SequenceAt = number | `+${number}` | `-${number}` | \"<\" | string;\n\nexport interface SegmentOptions extends AnimationOptions {\n\treadonly at?: SequenceAt;\n}\n\nexport type Segment =\n\t| readonly [ElementTarget, DOMKeyframes, SegmentOptions?]\n\t// biome-ignore lint/suspicious/noExplicitAny: a sequence mixes values of any type.\n\t| readonly [MotionValue<any>, Keyframes<any>, SegmentOptions?]\n\t| string\n\t| { readonly name: string; readonly at?: SequenceAt };\n\nexport type AnimateOne = (\n\ttarget: unknown,\n\tkeyframes: unknown,\n\toptions: AnimationOptions,\n) => AnimationControls;\n\nexport type ResolveTargets = (target: unknown) => readonly object[];\n\nfunction startOf(\n\tat: SequenceAt | undefined,\n\tcursor: number,\n\tpreviousStart: number,\n\tlabels: ReadonlyMap<string, number>,\n): number {\n\tif (at === undefined) return cursor;\n\tif (typeof at === \"number\") return at;\n\tif (at === \"<\") return previousStart;\n\tif (/^[+-]/.test(at)) return Math.max(0, cursor + Number(at));\n\treturn labels.get(at) ?? cursor;\n}\n\n/**\n * How long a segment that waits for an earlier one will run once launched.\n * Its first keyframe is only read then, but a tween's length doesn't depend\n * on it, and neither does a default spring's (its rest thresholds scale with\n * the distance).\n */\nfunction plannedDuration(keyframes: unknown, options: AnimationOptions) {\n\tconst channels =\n\t\ttypeof keyframes === \"object\" &&\n\t\tkeyframes !== null &&\n\t\t!Array.isArray(keyframes)\n\t\t\t? Object.values(keyframes)\n\t\t\t: [keyframes];\n\tconst { delay } = options;\n\treturn (\n\t\t(typeof delay === \"number\" ? delay : 0) +\n\t\tMath.max(\n\t\t\t0,\n\t\t\t...channels.map((channel) => {\n\t\t\t\tconst frames = Array.isArray(channel) ? channel : [0, channel];\n\t\t\t\tconst { generator } = planGenerator(\n\t\t\t\t\tframes.map((_frame, index) => index),\n\t\t\t\t\toptions,\n\t\t\t\t);\n\t\t\t\treturn totalDuration(generator.duration, options);\n\t\t\t}),\n\t\t)\n\t);\n}\n\n/**\n * Launches every segment at once, each delayed to its place on the\n * timeline, so the whole sequence shares one clock and can be scrubbed. A\n * segment that animates a property an earlier segment already animates\n * waits for that one to finish, then starts from where it left off.\n */\nexport function animateSequence(\n\tsegments: readonly Segment[],\n\toptions: AnimationOptions,\n\tanimateOne: AnimateOne,\n\tresolveTargets: ResolveTargets,\n): AnimationControls {\n\t/** The sequence's own clock: it runs at the speed the sequence is set to. */\n\tlet rate = 1;\n\tlet base = 0;\n\tlet since = now();\n\tlet paused = false;\n\t/** Stopped or cancelled: a segment still waiting never starts. */\n\tlet halted = false;\n\tlet completing = false;\n\tfunction elapsed(): number {\n\t\treturn paused ? base : base + ((now() - since) / 1000) * rate;\n\t}\n\tfunction retime(seconds: number) {\n\t\tbase = seconds;\n\t\tsince = now();\n\t}\n\tconst labels = new Map<string, number>();\n\tconst busy = new Map<\n\t\tobject,\n\t\tMap<string, { readonly finished: Promise<void>; readonly end: number }>\n\t>();\n\t/** Live: a segment that waited joins it once launched, and the controls reach it. */\n\tconst children: AnimationControls[] = [];\n\t/** The sequence time each late segment's own time counts from. */\n\tconst origins = new Map<AnimationControls, number>();\n\t/** Opens the segments still waiting that a seek reaches, in order. */\n\tconst openers: ((seconds: number) => void)[] = [];\n\tconst waits: Promise<void>[] = [];\n\tlet end = 0;\n\tlet cursor = 0;\n\tlet previousStart = 0;\n\tfor (const segment of segments) {\n\t\tif (typeof segment === \"string\" || \"name\" in segment) {\n\t\t\tconst label = typeof segment === \"string\" ? { name: segment } : segment;\n\t\t\tlabels.set(label.name, startOf(label.at, cursor, previousStart, labels));\n\t\t\tcontinue;\n\t\t}\n\t\tconst [target, keyframes, own = {}] = segment as readonly [\n\t\t\tunknown,\n\t\t\tunknown,\n\t\t\tSegmentOptions?,\n\t\t];\n\t\tconst placed = startOf(own.at, cursor, previousStart, labels);\n\t\tconst keys =\n\t\t\ttypeof keyframes === \"object\" &&\n\t\t\tkeyframes !== null &&\n\t\t\t!Array.isArray(keyframes)\n\t\t\t\t? Object.keys(keyframes)\n\t\t\t\t: [\"value\"];\n\t\tconst targets = resolveTargets(target);\n\t\tconst before = targets.flatMap((one) =>\n\t\t\tkeys.flatMap((key) => busy.get(one)?.get(key) ?? []),\n\t\t);\n\t\tconst start = Math.max(placed, ...before.map((earlier) => earlier.end));\n\t\tconst { delay } = own;\n\t\t/** Launched late, a segment only waits for what is left of its delay. */\n\t\tfunction optionsAt(elapsed: number): AnimationOptions {\n\t\t\tconst offset = start - elapsed;\n\t\t\treturn {\n\t\t\t\t...options,\n\t\t\t\t...own,\n\t\t\t\tdelay:\n\t\t\t\t\ttypeof delay === \"function\"\n\t\t\t\t\t\t? (index, total) => Math.max(0, offset + delay(index, total))\n\t\t\t\t\t\t: Math.max(0, offset + (delay ?? 0)),\n\t\t\t};\n\t\t}\n\t\tfunction launch(): AnimationControls {\n\t\t\tconst controls = animateOne(target, keyframes, optionsAt(elapsed()));\n\t\t\tcontrols.speed = rate;\n\t\t\treturn controls;\n\t\t}\n\t\tlet finished: Promise<void>;\n\t\tlet ends: number;\n\t\tif (before.length === 0) {\n\t\t\tconst controls = launch();\n\t\t\tchildren.push(controls);\n\t\t\tfinished = controls.finished;\n\t\t\tends = Math.max(start, controls.duration);\n\t\t} else {\n\t\t\tconst begins = start + (typeof delay === \"number\" ? delay : 0);\n\t\t\tlet opened: AnimationControls | undefined;\n\t\t\t// Played backwards past it, stopped or cancelled, it never starts.\n\t\t\tfunction open(): Promise<void> | undefined {\n\t\t\t\tif (opened || halted || rate < 0) return opened?.finished;\n\t\t\t\tconst at = elapsed();\n\t\t\t\topened = launch();\n\t\t\t\tconst origin = Math.min(at, begins);\n\t\t\t\torigins.set(opened, origin);\n\t\t\t\topened.time = at - origin;\n\t\t\t\tif (paused) opened.pause();\n\t\t\t\tif (completing) opened.complete();\n\t\t\t\tchildren.push(opened);\n\t\t\t\treturn opened.finished;\n\t\t\t}\n\t\t\topeners.push((seconds) => {\n\t\t\t\tif (seconds >= begins) open();\n\t\t\t});\n\t\t\tfinished = Promise.all(before.map((earlier) => earlier.finished)).then(\n\t\t\t\topen,\n\t\t\t);\n\t\t\twaits.push(finished);\n\t\t\tends = start + plannedDuration(keyframes, { ...options, ...own });\n\t\t}\n\t\tcursor = ends;\n\t\tend = Math.max(end, ends);\n\t\tfor (const one of targets) {\n\t\t\tconst byKey = busy.get(one) ?? new Map();\n\t\t\tbusy.set(one, byKey);\n\t\t\tfor (const key of keys) byKey.set(key, { finished, end: ends });\n\t\t}\n\t\tpreviousStart = start;\n\t}\n\tconst group = groupControls(children);\n\tconst finished = Promise.all([group.finished, ...waits]).then(\n\t\t() => undefined,\n\t);\n\t/** The group's own action, after the sequence's clock or flags have moved. */\n\tfunction around(action: keyof AnimationControls, first: () => void) {\n\t\tconst own = group[action] as () => void;\n\t\treturn {\n\t\t\tvalue() {\n\t\t\t\tfirst();\n\t\t\t\town();\n\t\t\t},\n\t\t};\n\t}\n\tObject.defineProperties(group, {\n\t\ttime: {\n\t\t\tget: elapsed,\n\t\t\tset(seconds: number) {\n\t\t\t\tretime(seconds);\n\t\t\t\tfor (const child of children) {\n\t\t\t\t\tchild.time = seconds - (origins.get(child) ?? 0);\n\t\t\t\t}\n\t\t\t\tfor (const opener of openers) opener(seconds);\n\t\t\t},\n\t\t},\n\t\tpause: around(\"pause\", () => {\n\t\t\tretime(elapsed());\n\t\t\tpaused = true;\n\t\t}),\n\t\tplay: around(\"play\", () => {\n\t\t\tretime(elapsed());\n\t\t\tpaused = false;\n\t\t}),\n\t\tstop: around(\"stop\", () => {\n\t\t\thalted = true;\n\t\t}),\n\t\tcancel: around(\"cancel\", () => {\n\t\t\thalted = true;\n\t\t}),\n\t\tcomplete: around(\"complete\", () => {\n\t\t\tcompleting = true;\n\t\t}),\n\t\t// The segments still waiting count too: they end after the ones launched.\n\t\tduration: { value: Math.max(end, group.duration) },\n\t\tspeed: {\n\t\t\tget: () => rate,\n\t\t\tset(next: number) {\n\t\t\t\tretime(elapsed());\n\t\t\t\trate = next;\n\t\t\t\tfor (const child of children) child.speed = next;\n\t\t\t},\n\t\t},\n\t});\n\treturn Object.assign(group, thenable(finished));\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["optionsAt", "elapsed", "launch", "finished", "open"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voila.dev/motion",
3
- "version": "6.0.124",
3
+ "version": "6.0.126",
4
4
  "type": "module",
5
5
  "description": "Springs played by the browser, values on one frame loop, exits React waits for.",
6
6
  "license": "MIT",
@@ -95,17 +95,28 @@ export function animateSequence(
95
95
  let rate = 1;
96
96
  let base = 0;
97
97
  let since = now();
98
+ let paused = false;
99
+ /** Stopped or cancelled: a segment still waiting never starts. */
100
+ let halted = false;
101
+ let completing = false;
98
102
  function elapsed(): number {
99
- return base + ((now() - since) / 1000) * rate;
103
+ return paused ? base : base + ((now() - since) / 1000) * rate;
104
+ }
105
+ function retime(seconds: number) {
106
+ base = seconds;
107
+ since = now();
100
108
  }
101
109
  const labels = new Map<string, number>();
102
110
  const busy = new Map<
103
111
  object,
104
112
  Map<string, { readonly finished: Promise<void>; readonly end: number }>
105
113
  >();
114
+ /** Live: a segment that waited joins it once launched, and the controls reach it. */
106
115
  const children: AnimationControls[] = [];
107
- /** The segments that waited, once launched: they take later speed changes too. */
108
- const late: AnimationControls[] = [];
116
+ /** The sequence time each late segment's own time counts from. */
117
+ const origins = new Map<AnimationControls, number>();
118
+ /** Opens the segments still waiting that a seek reaches, in order. */
119
+ const openers: ((seconds: number) => void)[] = [];
109
120
  const waits: Promise<void>[] = [];
110
121
  let end = 0;
111
122
  let cursor = 0;
@@ -159,14 +170,26 @@ export function animateSequence(
159
170
  finished = controls.finished;
160
171
  ends = Math.max(start, controls.duration);
161
172
  } else {
162
- // Played backwards, the sequence went back past it: it never starts.
173
+ const begins = start + (typeof delay === "number" ? delay : 0);
174
+ let opened: AnimationControls | undefined;
175
+ // Played backwards past it, stopped or cancelled, it never starts.
176
+ function open(): Promise<void> | undefined {
177
+ if (opened || halted || rate < 0) return opened?.finished;
178
+ const at = elapsed();
179
+ opened = launch();
180
+ const origin = Math.min(at, begins);
181
+ origins.set(opened, origin);
182
+ opened.time = at - origin;
183
+ if (paused) opened.pause();
184
+ if (completing) opened.complete();
185
+ children.push(opened);
186
+ return opened.finished;
187
+ }
188
+ openers.push((seconds) => {
189
+ if (seconds >= begins) open();
190
+ });
163
191
  finished = Promise.all(before.map((earlier) => earlier.finished)).then(
164
- () => {
165
- if (rate < 0) return;
166
- const controls = launch();
167
- late.push(controls);
168
- return controls.finished;
169
- },
192
+ open,
170
193
  );
171
194
  waits.push(finished);
172
195
  ends = start + plannedDuration(keyframes, { ...options, ...own });
@@ -184,16 +207,52 @@ export function animateSequence(
184
207
  const finished = Promise.all([group.finished, ...waits]).then(
185
208
  () => undefined,
186
209
  );
210
+ /** The group's own action, after the sequence's clock or flags have moved. */
211
+ function around(action: keyof AnimationControls, first: () => void) {
212
+ const own = group[action] as () => void;
213
+ return {
214
+ value() {
215
+ first();
216
+ own();
217
+ },
218
+ };
219
+ }
187
220
  Object.defineProperties(group, {
221
+ time: {
222
+ get: elapsed,
223
+ set(seconds: number) {
224
+ retime(seconds);
225
+ for (const child of children) {
226
+ child.time = seconds - (origins.get(child) ?? 0);
227
+ }
228
+ for (const opener of openers) opener(seconds);
229
+ },
230
+ },
231
+ pause: around("pause", () => {
232
+ retime(elapsed());
233
+ paused = true;
234
+ }),
235
+ play: around("play", () => {
236
+ retime(elapsed());
237
+ paused = false;
238
+ }),
239
+ stop: around("stop", () => {
240
+ halted = true;
241
+ }),
242
+ cancel: around("cancel", () => {
243
+ halted = true;
244
+ }),
245
+ complete: around("complete", () => {
246
+ completing = true;
247
+ }),
188
248
  // The segments still waiting count too: they end after the ones launched.
189
249
  duration: { value: Math.max(end, group.duration) },
190
250
  speed: {
191
251
  get: () => rate,
192
252
  set(next: number) {
193
- base = elapsed();
194
- since = now();
253
+ retime(elapsed());
195
254
  rate = next;
196
- for (const child of [...children, ...late]) child.speed = next;
255
+ for (const child of children) child.speed = next;
197
256
  },
198
257
  },
199
258
  });