@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,
|
|
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
|
|
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
|
-
|
|
108
|
-
since = now();
|
|
164
|
+
retime(elapsed());
|
|
109
165
|
rate = next;
|
|
110
|
-
for (const child of
|
|
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
|
|
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,
|
|
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
package/src/sequence/sequence.ts
CHANGED
|
@@ -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
|
|
108
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
194
|
-
since = now();
|
|
253
|
+
retime(elapsed());
|
|
195
254
|
rate = next;
|
|
196
|
-
for (const child of
|
|
255
|
+
for (const child of children) child.speed = next;
|
|
197
256
|
},
|
|
198
257
|
},
|
|
199
258
|
});
|