@sliiu/present-transition-fx 0.0.0-stage → 0.1.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.
Files changed (48) hide show
  1. package/COPYRIGHT +5 -0
  2. package/LICENSE +661 -0
  3. package/THIRD-PARTY-NOTICES.md +221 -0
  4. package/dist/cjs/2d.js +487 -0
  5. package/dist/cjs/3d.js +566 -0
  6. package/dist/cjs/geometry.js +141 -0
  7. package/dist/cjs/index.js +98 -0
  8. package/dist/cjs/morph-audit.js +603 -0
  9. package/dist/cjs/morph-match.js +103 -0
  10. package/dist/cjs/morph-path.js +649 -0
  11. package/dist/cjs/morph.js +223 -0
  12. package/dist/cjs/package.json +1 -0
  13. package/dist/cjs/registry.js +130 -0
  14. package/dist/cjs/snapshot.js +420 -0
  15. package/dist/cjs/twin.js +88 -0
  16. package/dist/cjs/types.js +2 -0
  17. package/dist/cjs/util.js +50 -0
  18. package/dist/cjs/webgl.js +271 -0
  19. package/dist/esm/2d.js +454 -0
  20. package/dist/esm/3d.js +538 -0
  21. package/dist/esm/geometry.js +127 -0
  22. package/dist/esm/index.js +6 -0
  23. package/dist/esm/morph-audit.js +558 -0
  24. package/dist/esm/morph-match.js +97 -0
  25. package/dist/esm/morph-path.js +636 -0
  26. package/dist/esm/morph.js +219 -0
  27. package/dist/esm/registry.js +91 -0
  28. package/dist/esm/snapshot.js +414 -0
  29. package/dist/esm/twin.js +84 -0
  30. package/dist/esm/types.js +1 -0
  31. package/dist/esm/util.js +43 -0
  32. package/dist/esm/webgl.js +231 -0
  33. package/dist/types/2d.d.ts +40 -0
  34. package/dist/types/3d.d.ts +38 -0
  35. package/dist/types/geometry.d.ts +38 -0
  36. package/dist/types/index.d.ts +7 -0
  37. package/dist/types/morph-audit.d.ts +127 -0
  38. package/dist/types/morph-match.d.ts +28 -0
  39. package/dist/types/morph-path.d.ts +37 -0
  40. package/dist/types/morph.d.ts +2 -0
  41. package/dist/types/registry.d.ts +6 -0
  42. package/dist/types/snapshot.d.ts +19 -0
  43. package/dist/types/twin.d.ts +2 -0
  44. package/dist/types/types.d.ts +13 -0
  45. package/dist/types/util.d.ts +12 -0
  46. package/dist/types/webgl.d.ts +5 -0
  47. package/package.json +26 -4
  48. package/README.md +0 -3
@@ -0,0 +1,420 @@
1
+ "use strict";
2
+ // WebGL 切换要把整页画进纹理。foreignObject 再画到 canvas 时,SVG 里任何
3
+ // 外部地址(包括本页 createObjectURL 的 blob:)都会把画布弄脏,随后
4
+ // texImage2D 抛 SecurityError。这里先把能读到像素的图换成 data URL;
5
+ // 读不到的可见图返回 false,调用方改走不读像素的 CSS 切换。
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.isExternalSnapshotUrl = isExternalSnapshotUrl;
8
+ exports.rewriteCssForSnapshot = rewriteCssForSnapshot;
9
+ exports.sanitizeSnapshotClone = sanitizeSnapshotClone;
10
+ exports.snapshotElement = snapshotElement;
11
+ const XLINK_NS = "http://www.w3.org/1999/xlink";
12
+ const URL_ATTRS = ["href", "src", "poster"];
13
+ function isExternalSnapshotUrl(url) {
14
+ const u = url.trim();
15
+ if (!u || u.startsWith("#") || /^data:/i.test(u) || /^about:/i.test(u))
16
+ return false;
17
+ return true;
18
+ }
19
+ /** 把样式里的外部 url() 换成 data URL;换不掉的可见背景改成 none,避免再去拉取。 */
20
+ function rewriteCssForSnapshot(css, resolved) {
21
+ let droppedVisible = false;
22
+ // @import 会让 SVG 再去拉外部样式,把画布弄脏。样式表本身不是画面内容,去掉即可。
23
+ const noImport = css.replace(/@import\b[^;]*;/gi, "");
24
+ const cssUrl = /url\(\s*(['"]?)([^'")]+)\1\s*\)/gi;
25
+ const next = noImport.replace(cssUrl, (full, _quote, raw, index) => {
26
+ const url = String(raw).trim();
27
+ if (!isExternalSnapshotUrl(url))
28
+ return full;
29
+ const data = resolved.get(url);
30
+ if (data)
31
+ return `url("${data}")`;
32
+ const before = noImport.slice(Math.max(0, index - 48), index).toLowerCase();
33
+ if (/background|mask|border-image|content\s*:/.test(before))
34
+ droppedVisible = true;
35
+ return "none";
36
+ });
37
+ return { css: next, droppedVisible };
38
+ }
39
+ function bytesToBase64(bytes) {
40
+ const parts = [];
41
+ const chunk = 0x8000;
42
+ for (let i = 0; i < bytes.length; i += chunk) {
43
+ const slice = bytes.subarray(i, i + chunk);
44
+ const codes = new Array(slice.length);
45
+ for (let j = 0; j < slice.length; j++)
46
+ codes[j] = String.fromCharCode(slice[j]);
47
+ parts.push(codes.join(""));
48
+ }
49
+ return btoa(parts.join(""));
50
+ }
51
+ async function blobToDataUrl(blob) {
52
+ if (typeof FileReader !== "undefined") {
53
+ const viaReader = await new Promise((resolve) => {
54
+ const reader = new FileReader();
55
+ reader.onload = () => resolve(typeof reader.result === "string" ? reader.result : null);
56
+ reader.onerror = () => resolve(null);
57
+ try {
58
+ reader.readAsDataURL(blob);
59
+ }
60
+ catch {
61
+ resolve(null);
62
+ }
63
+ });
64
+ if (viaReader?.startsWith("data:"))
65
+ return viaReader;
66
+ }
67
+ try {
68
+ const bytes = new Uint8Array(await blob.arrayBuffer());
69
+ const type = blob.type || "application/octet-stream";
70
+ return `data:${type};base64,${bytesToBase64(bytes)}`;
71
+ }
72
+ catch {
73
+ return null;
74
+ }
75
+ }
76
+ function imageConstructor(doc) {
77
+ const Ctor = doc.defaultView?.Image ?? globalThis.Image;
78
+ return typeof Ctor === "function" ? Ctor : null;
79
+ }
80
+ function drawImageToDataUrl(url, doc) {
81
+ const Ctor = imageConstructor(doc);
82
+ if (!Ctor)
83
+ return Promise.resolve(null);
84
+ return new Promise((resolve) => {
85
+ const img = new Ctor();
86
+ if (/^https?:\/\//i.test(url))
87
+ img.crossOrigin = "anonymous";
88
+ let settled = false;
89
+ let timer;
90
+ const finish = (value) => {
91
+ if (settled)
92
+ return;
93
+ settled = true;
94
+ if (timer !== undefined)
95
+ clearTimeout(timer);
96
+ resolve(value);
97
+ };
98
+ timer = setTimeout(() => finish(null), 2000);
99
+ img.onload = () => {
100
+ try {
101
+ const canvas = doc.createElement("canvas");
102
+ const w = img.naturalWidth || img.width;
103
+ const h = img.naturalHeight || img.height;
104
+ if (w <= 0 || h <= 0) {
105
+ finish(null);
106
+ return;
107
+ }
108
+ canvas.width = w;
109
+ canvas.height = h;
110
+ canvas.getContext("2d")?.drawImage(img, 0, 0, w, h);
111
+ const data = canvas.toDataURL("image/png");
112
+ finish(data.startsWith("data:") ? data : null);
113
+ }
114
+ catch {
115
+ finish(null);
116
+ }
117
+ };
118
+ img.onerror = () => finish(null);
119
+ img.src = url;
120
+ });
121
+ }
122
+ async function readAsDataUrl(url, doc) {
123
+ try {
124
+ const res = await fetch(url);
125
+ if (res.ok) {
126
+ const data = await blobToDataUrl(await res.blob());
127
+ if (data)
128
+ return data;
129
+ }
130
+ }
131
+ catch {
132
+ // 跨域且没有 CORS 头时 fetch 会失败,下面再用带 anonymous 的 Image 试一次。
133
+ }
134
+ return drawImageToDataUrl(url, doc);
135
+ }
136
+ function cachedDataUrl(url, doc, cache) {
137
+ const hit = cache.get(url);
138
+ if (hit)
139
+ return hit;
140
+ const pending = readAsDataUrl(url, doc).catch(() => null);
141
+ cache.set(url, pending);
142
+ return pending;
143
+ }
144
+ function elementsOf(root, selector) {
145
+ const out = [];
146
+ if (root.matches(selector))
147
+ out.push(root);
148
+ root.querySelectorAll(selector).forEach((el) => out.push(el));
149
+ return out;
150
+ }
151
+ function copyLayout(from, to) {
152
+ const style = from.getAttribute("style");
153
+ if (style)
154
+ to.setAttribute("style", style);
155
+ const width = from.getAttribute("width");
156
+ const height = from.getAttribute("height");
157
+ if (width)
158
+ to.setAttribute("width", width);
159
+ if (height)
160
+ to.setAttribute("height", height);
161
+ }
162
+ function stillFromCanvas(canvas, doc) {
163
+ if (canvas.width <= 0 || canvas.height <= 0)
164
+ return null;
165
+ try {
166
+ const url = canvas.toDataURL("image/png");
167
+ if (!url.startsWith("data:"))
168
+ return null;
169
+ const img = doc.createElement("img");
170
+ img.src = url;
171
+ copyLayout(canvas, img);
172
+ return img;
173
+ }
174
+ catch {
175
+ return null;
176
+ }
177
+ }
178
+ function stillFromVideo(video, doc) {
179
+ const w = video.videoWidth || video.clientWidth;
180
+ const h = video.videoHeight || video.clientHeight;
181
+ if (w <= 0 || h <= 0)
182
+ return null;
183
+ try {
184
+ const canvas = doc.createElement("canvas");
185
+ canvas.width = w;
186
+ canvas.height = h;
187
+ const cx = canvas.getContext("2d");
188
+ if (!cx)
189
+ return null;
190
+ cx.drawImage(video, 0, 0, w, h);
191
+ const url = canvas.toDataURL("image/jpeg", 0.72);
192
+ if (!url.startsWith("data:"))
193
+ return null;
194
+ const img = doc.createElement("img");
195
+ img.src = url;
196
+ copyLayout(video, img);
197
+ return img;
198
+ }
199
+ catch {
200
+ return null;
201
+ }
202
+ }
203
+ function walk(root, visit) {
204
+ visit(root);
205
+ root.querySelectorAll("*").forEach(visit);
206
+ }
207
+ function collectExternalUrls(root) {
208
+ const urls = new Set();
209
+ const take = (value) => {
210
+ if (value && isExternalSnapshotUrl(value))
211
+ urls.add(value.trim());
212
+ };
213
+ walk(root, (el) => {
214
+ if (el.localName === "style" && el.textContent) {
215
+ const cssUrl = /url\(\s*(['"]?)([^'")]+)\1\s*\)/gi;
216
+ for (const match of el.textContent.matchAll(cssUrl)) {
217
+ const raw = match[2];
218
+ if (raw && isExternalSnapshotUrl(raw))
219
+ urls.add(raw.trim());
220
+ }
221
+ }
222
+ const style = el.getAttribute("style");
223
+ if (style) {
224
+ const cssUrl = /url\(\s*(['"]?)([^'")]+)\1\s*\)/gi;
225
+ for (const match of style.matchAll(cssUrl)) {
226
+ const raw = match[2];
227
+ if (raw && isExternalSnapshotUrl(raw))
228
+ urls.add(raw.trim());
229
+ }
230
+ }
231
+ for (const name of URL_ATTRS)
232
+ take(el.getAttribute(name));
233
+ take(el.getAttributeNS(XLINK_NS, "href"));
234
+ });
235
+ return urls;
236
+ }
237
+ function applyResolvedUrls(root, resolved) {
238
+ let complete = true;
239
+ const paintTag = (el) => el.localName === "image" || el.localName === "img" || el.localName === "video" || el.localName === "source";
240
+ const writeAttr = (el, name, ns, value) => {
241
+ if (!value || !isExternalSnapshotUrl(value))
242
+ return;
243
+ const data = resolved.get(value.trim());
244
+ if (data) {
245
+ if (ns)
246
+ el.setAttributeNS(ns, name, data);
247
+ else
248
+ el.setAttribute(name, data);
249
+ return;
250
+ }
251
+ if (ns)
252
+ el.removeAttributeNS(ns, name);
253
+ else
254
+ el.removeAttribute(name);
255
+ if (paintTag(el))
256
+ complete = false;
257
+ };
258
+ walk(root, (el) => {
259
+ if (el.hasAttribute("srcset"))
260
+ el.removeAttribute("srcset");
261
+ if (el.localName === "style" && el.textContent) {
262
+ const rewritten = rewriteCssForSnapshot(el.textContent, resolved);
263
+ el.textContent = rewritten.css;
264
+ if (rewritten.droppedVisible)
265
+ complete = false;
266
+ }
267
+ const style = el.getAttribute("style");
268
+ if (style) {
269
+ const rewritten = rewriteCssForSnapshot(style, resolved);
270
+ el.setAttribute("style", rewritten.css);
271
+ if (rewritten.droppedVisible)
272
+ complete = false;
273
+ }
274
+ for (const name of URL_ATTRS)
275
+ writeAttr(el, name, null, el.getAttribute(name));
276
+ writeAttr(el, "href", XLINK_NS, el.getAttributeNS(XLINK_NS, "href"));
277
+ });
278
+ return complete;
279
+ }
280
+ /**
281
+ * 在克隆树上把外部图片换成 data URL,并拆掉会弄脏画布的 video / iframe。
282
+ * 返回 false 表示有一张可见图读不出来,快照不能代表这一页。
283
+ * 不改 live。
284
+ */
285
+ async function sanitizeSnapshotClone(live, clone, cache = new Map()) {
286
+ const doc = clone.ownerDocument;
287
+ let complete = true;
288
+ const liveCanvases = elementsOf(live, "canvas");
289
+ const cloneCanvases = elementsOf(clone, "canvas");
290
+ const canvasCount = Math.min(liveCanvases.length, cloneCanvases.length);
291
+ if (liveCanvases.length !== cloneCanvases.length)
292
+ complete = false;
293
+ for (let i = 0; i < canvasCount; i++) {
294
+ const src = liveCanvases[i];
295
+ const dst = cloneCanvases[i];
296
+ if (!src || !dst)
297
+ continue;
298
+ if (src.width <= 0 || src.height <= 0)
299
+ continue;
300
+ const still = stillFromCanvas(src, doc);
301
+ if (still)
302
+ dst.replaceWith(still);
303
+ else
304
+ complete = false;
305
+ }
306
+ const liveVideos = elementsOf(live, "video");
307
+ const cloneVideos = elementsOf(clone, "video");
308
+ const videoCount = Math.min(liveVideos.length, cloneVideos.length);
309
+ if (liveVideos.length !== cloneVideos.length)
310
+ complete = false;
311
+ for (let i = 0; i < videoCount; i++) {
312
+ const src = liveVideos[i];
313
+ const dst = cloneVideos[i];
314
+ if (!src || !dst)
315
+ continue;
316
+ const still = stillFromVideo(src, doc);
317
+ if (still) {
318
+ dst.replaceWith(still);
319
+ continue;
320
+ }
321
+ const poster = src.poster || dst.getAttribute("poster");
322
+ if (poster) {
323
+ const img = doc.createElement("img");
324
+ img.setAttribute("src", poster);
325
+ copyLayout(dst, img);
326
+ dst.replaceWith(img);
327
+ continue;
328
+ }
329
+ dst.remove();
330
+ complete = false;
331
+ }
332
+ if (clone.querySelector("iframe, object, embed")) {
333
+ complete = false;
334
+ clone.querySelectorAll("iframe, object, embed").forEach((el) => el.remove());
335
+ }
336
+ clone.querySelectorAll("audio, link[rel='stylesheet'], link[rel='preconnect']").forEach((el) => el.remove());
337
+ const urls = collectExternalUrls(clone);
338
+ const resolved = new Map();
339
+ await Promise.all([...urls].map(async (url) => {
340
+ resolved.set(url, await cachedDataUrl(url, doc, cache));
341
+ }));
342
+ if (!applyResolvedUrls(clone, resolved))
343
+ complete = false;
344
+ return complete;
345
+ }
346
+ function canvasIsReadable(canvas) {
347
+ try {
348
+ const cx = canvas.getContext("2d");
349
+ if (!cx || canvas.width === 0 || canvas.height === 0)
350
+ return false;
351
+ cx.getImageData(0, 0, 1, 1);
352
+ return true;
353
+ }
354
+ catch {
355
+ return false;
356
+ }
357
+ }
358
+ function loadImage(doc, url) {
359
+ const Ctor = imageConstructor(doc);
360
+ if (!Ctor)
361
+ return Promise.resolve(null);
362
+ return new Promise((resolve) => {
363
+ const img = new Ctor();
364
+ let settled = false;
365
+ let timer;
366
+ const finish = (value) => {
367
+ if (settled)
368
+ return;
369
+ settled = true;
370
+ if (timer !== undefined)
371
+ clearTimeout(timer);
372
+ resolve(value);
373
+ };
374
+ timer = setTimeout(() => finish(null), 3000);
375
+ img.onload = () => finish(img);
376
+ img.onerror = () => finish(null);
377
+ img.src = url;
378
+ });
379
+ }
380
+ async function snapshotElement(el, w, h, cache = new Map()) {
381
+ if (w <= 0 || h <= 0)
382
+ return null;
383
+ const doc = el.ownerDocument;
384
+ try {
385
+ const clone = el.cloneNode(true);
386
+ if (clone.namespaceURI === "http://www.w3.org/1999/xhtml" && !clone.getAttribute("xmlns")) {
387
+ clone.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
388
+ }
389
+ const complete = await sanitizeSnapshotClone(el, clone, cache);
390
+ if (!complete)
391
+ return null;
392
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}">` +
393
+ `<foreignObject width="100%" height="100%">` +
394
+ `${new XMLSerializer().serializeToString(clone)}` +
395
+ `</foreignObject></svg>`;
396
+ const blob = new Blob([svg], { type: "image/svg+xml;charset=utf-8" });
397
+ const url = URL.createObjectURL(blob);
398
+ try {
399
+ const img = await loadImage(doc, url);
400
+ if (!img)
401
+ return null;
402
+ const canvas = doc.createElement("canvas");
403
+ canvas.width = w;
404
+ canvas.height = h;
405
+ const cx = canvas.getContext("2d");
406
+ if (!cx)
407
+ return null;
408
+ cx.drawImage(img, 0, 0, w, h);
409
+ if (!canvasIsReadable(canvas))
410
+ return null;
411
+ return { canvas, width: w, height: h };
412
+ }
413
+ finally {
414
+ URL.revokeObjectURL(url);
415
+ }
416
+ }
417
+ catch {
418
+ return null;
419
+ }
420
+ }
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ // 把一页画面克隆成若干裁切块。门、百叶窗、梳理要用旧页自己的内容,不能涂黑块。
3
+ // id 加前缀,避免和还在树上的原节点抢 url(#…)。
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.mountTwin = mountTwin;
6
+ exports.dropTwins = dropTwins;
7
+ let twinSeq = 0;
8
+ function mountTwin(source, clip) {
9
+ const twin = source.cloneNode(true);
10
+ const prefix = `__tw${++twinSeq}_`;
11
+ const idMap = new Map();
12
+ const take = (el) => {
13
+ const old = el.getAttribute("id");
14
+ if (!old)
15
+ return;
16
+ const next = prefix + old;
17
+ el.setAttribute("id", next);
18
+ idMap.set(old, next);
19
+ };
20
+ take(twin);
21
+ twin.querySelectorAll("[id]").forEach(take);
22
+ if (idMap.size > 0)
23
+ rewriteLocalRefs(twin, idMap);
24
+ copyCanvasBitmaps(source, twin);
25
+ twin.style.clipPath = clip;
26
+ twin.style.visibility = "visible";
27
+ twin.style.pointerEvents = "none";
28
+ twin.style.zIndex = source.style.zIndex || "2";
29
+ source.parentElement?.appendChild(twin);
30
+ return twin;
31
+ }
32
+ function dropTwins(twins, source, prevVisibility) {
33
+ for (const t of twins)
34
+ t.remove();
35
+ source.style.visibility = prevVisibility;
36
+ }
37
+ function copyCanvasBitmaps(src, dst) {
38
+ const srcCanvases = src.querySelectorAll("canvas");
39
+ const dstCanvases = dst.querySelectorAll("canvas");
40
+ const n = Math.min(srcCanvases.length, dstCanvases.length);
41
+ for (let i = 0; i < n; i++) {
42
+ const s = srcCanvases[i];
43
+ const d = dstCanvases[i];
44
+ if (s.width === 0 || s.height === 0)
45
+ continue;
46
+ d.width = s.width;
47
+ d.height = s.height;
48
+ const ctx = d.getContext("2d");
49
+ if (!ctx)
50
+ continue;
51
+ try {
52
+ ctx.drawImage(s, 0, 0);
53
+ }
54
+ catch {
55
+ // 跨域画布画不上,这块保持空白。
56
+ }
57
+ }
58
+ }
59
+ function rewriteLocalRefs(root, idMap) {
60
+ const xlinkNs = "http://www.w3.org/1999/xlink";
61
+ const replaceUrl = (s) => s.replace(/url\(\s*#([^)\s"']+)\s*\)/g, (full, id) => {
62
+ const next = idMap.get(id);
63
+ return next ? `url(#${next})` : full;
64
+ });
65
+ const visit = (el) => {
66
+ for (let i = 0; i < el.attributes.length; i++) {
67
+ const a = el.attributes[i];
68
+ if (a.name === "id")
69
+ continue;
70
+ if (a.value.includes("url(#"))
71
+ a.value = replaceUrl(a.value);
72
+ else if ((a.localName === "href" || a.name === "href") && a.value.startsWith("#")) {
73
+ const next = idMap.get(a.value.slice(1));
74
+ if (next)
75
+ a.value = "#" + next;
76
+ }
77
+ }
78
+ const xhref = el.getAttributeNS(xlinkNs, "href");
79
+ if (xhref?.startsWith("#")) {
80
+ const next = idMap.get(xhref.slice(1));
81
+ if (next)
82
+ el.setAttributeNS(xlinkNs, "href", "#" + next);
83
+ }
84
+ for (const child of Array.from(el.children))
85
+ visit(child);
86
+ };
87
+ visit(root);
88
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LINEAR = exports.STD_EASE = void 0;
4
+ exports.dirVec = dirVec;
5
+ exports.runAll = runAll;
6
+ exports.ms = ms;
7
+ exports.easing = easing;
8
+ exports.STD_EASE = "cubic-bezier(0.4, 0, 0.2, 1)";
9
+ exports.LINEAR = "linear";
10
+ function dirVec(dir, w, h) {
11
+ switch (dir) {
12
+ case "left":
13
+ return { dx: w, dy: 0, axis: "x", sign: 1 };
14
+ case "right":
15
+ return { dx: -w, dy: 0, axis: "x", sign: -1 };
16
+ case "up":
17
+ return { dx: 0, dy: h, axis: "y", sign: 1 };
18
+ case "down":
19
+ return { dx: 0, dy: -h, axis: "y", sign: -1 };
20
+ case "topLeft":
21
+ return { dx: w, dy: h, axis: "x", sign: 1 };
22
+ case "topRight":
23
+ return { dx: -w, dy: h, axis: "x", sign: -1 };
24
+ case "bottomLeft":
25
+ return { dx: w, dy: -h, axis: "x", sign: 1 };
26
+ case "bottomRight":
27
+ return { dx: -w, dy: -h, axis: "x", sign: -1 };
28
+ default:
29
+ return { dx: w, dy: 0, axis: "x", sign: 1 };
30
+ }
31
+ }
32
+ async function runAll(anims, cleanup) {
33
+ await Promise.all(anims.map((a) => a.finished.catch(() => undefined)));
34
+ for (const a of anims) {
35
+ try {
36
+ a.commitStyles();
37
+ }
38
+ catch {
39
+ // ignore
40
+ }
41
+ a.cancel();
42
+ }
43
+ cleanup?.();
44
+ }
45
+ function ms(ctx) {
46
+ return Math.max(1, ctx.duration * 1000);
47
+ }
48
+ function easing(ctx) {
49
+ return ctx.params?.ease ?? exports.STD_EASE;
50
+ }