@voila.dev/motion 6.0.123 → 6.0.125

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":"dom.d.ts","sourceRoot":"","sources":["../../src/animate/dom.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAUhF,OAAO,KAAK,EACX,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,MAAM,oBAAoB,CAAC;AAuB5B;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,CAC7B,MAAM,EAAE,iBAAiB,CAAC,aAAa,CAAC,KACpC,WAAW,CAAC,aAAa,CAAC,CAAC;AAmGhC,qFAAqF;AACrF,wBAAgB,eAAe,CAC9B,QAAQ,EAAE,SAAS,OAAO,EAAE,EAC5B,SAAS,EAAE,YAAY,EACvB,OAAO,EAAE,gBAAgB,EACzB,QAAQ,CAAC,EAAE,eAAe,GACxB,iBAAiB,CAanB"}
1
+ {"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../../src/animate/dom.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAUhF,OAAO,KAAK,EACX,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,MAAM,oBAAoB,CAAC;AAuB5B;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,CAC7B,MAAM,EAAE,iBAAiB,CAAC,aAAa,CAAC,KACpC,WAAW,CAAC,aAAa,CAAC,CAAC;AA+GhC,qFAAqF;AACrF,wBAAgB,eAAe,CAC9B,QAAQ,EAAE,SAAS,OAAO,EAAE,EAC5B,SAAS,EAAE,YAAY,EACvB,OAAO,EAAE,gBAAgB,EACzB,QAAQ,CAAC,EAAE,eAAe,GACxB,iBAAiB,CAanB"}
@@ -28,6 +28,12 @@ const SVG_ATTRIBUTES = {
28
28
  attrWidth: "width",
29
29
  attrHeight: "height"
30
30
  };
31
+ const COMPOSITES = {
32
+ translate: "add",
33
+ scale: "add",
34
+ rotate: "add",
35
+ transform: "accumulate"
36
+ };
31
37
  const running = /* @__PURE__ */ new WeakMap();
32
38
  function channelOf(element, key) {
33
39
  const style = element.style;
@@ -86,7 +92,7 @@ function animateChannel(element, key, value, options, delay, scripted) {
86
92
  element,
87
93
  property: transform?.property ?? key,
88
94
  format: (frame) => transform ? transform.alone(withUnit(key, frame)) : withUnit(key, frame),
89
- composite: transform ? "add" : "replace",
95
+ composite: COMPOSITES[transform?.property ?? ""] ?? "replace",
90
96
  commit: channel.write
91
97
  }) : scripted({ ...config, apply: channel.write });
92
98
  animations.set(key, animation);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/animate/dom.ts"],
4
- "sourcesContent": ["import { shouldReduceMotion } from \"#/accessibility/reduced-motion.ts\";\nimport { finishedControls, groupControls } from \"#/animate/controls.ts\";\nimport type { JsAnimation, JsAnimationConfig } from \"#/animate/js-animation.ts\";\nimport {\n\tbeginTransform,\n\tcommitTransform,\n\tcommittedTransform,\n\tcssName,\n\tisMovement,\n\tTRANSFORMS,\n\twithUnit,\n} from \"#/animate/style.ts\";\nimport type {\n\tAnimationControls,\n\tAnimationOptions,\n\tDOMKeyframes,\n\tKeyframeValue,\n} from \"#/animate/types.ts\";\nimport { waapiAnimation } from \"#/animate/waapi.ts\";\n\n/** Attributes an SVG element animates in JavaScript: `d` morphs, the rest are geometry. */\nconst SVG_ATTRIBUTES: Readonly<Record<string, string>> = {\n\td: \"d\",\n\tpoints: \"points\",\n\tviewBox: \"viewBox\",\n\tcx: \"cx\",\n\tcy: \"cy\",\n\tr: \"r\",\n\trx: \"rx\",\n\try: \"ry\",\n\tx1: \"x1\",\n\ty1: \"y1\",\n\tx2: \"x2\",\n\ty2: \"y2\",\n\tattrX: \"x\",\n\tattrY: \"y\",\n\tattrWidth: \"width\",\n\tattrHeight: \"height\",\n};\n\n/**\n * Plays what the browser can't (SVG attributes, CSS variables) on the frame\n * loop. Injected, so an entry that only needs the browser's half never loads\n * the interpolators.\n */\nexport type ScriptedChannel = (\n\tconfig: JsAnimationConfig<KeyframeValue>,\n) => JsAnimation<KeyframeValue>;\n\nconst running = new WeakMap<Element, Map<string, JsAnimation<KeyframeValue>>>();\n\ninterface Channel {\n\tread(): KeyframeValue;\n\twrite(value: KeyframeValue): void;\n\treadonly native: boolean;\n}\n\nfunction channelOf(element: Element, key: string): Channel {\n\tconst style = (element as HTMLElement).style;\n\tconst attribute =\n\t\telement instanceof SVGElement ? SVG_ATTRIBUTES[key] : undefined;\n\tif (TRANSFORMS[key]) {\n\t\treturn {\n\t\t\tread: () => committedTransform(element, key),\n\t\t\twrite: (value) => commitTransform(element, key, value),\n\t\t\tnative: true,\n\t\t};\n\t}\n\tif (attribute) {\n\t\treturn {\n\t\t\tread: () => element.getAttribute(attribute) ?? \"\",\n\t\t\twrite: (value) => element.setAttribute(attribute, String(value)),\n\t\t\tnative: false,\n\t\t};\n\t}\n\tconst name = cssName(key);\n\treturn {\n\t\tread: () => getComputedStyle(element).getPropertyValue(name).trim(),\n\t\twrite: (value) => style.setProperty(name, withUnit(key, value)),\n\t\t// An unregistered custom property only animates discretely in the browser.\n\t\tnative: !key.startsWith(\"--\"),\n\t};\n}\n\nfunction asTarget(read: KeyframeValue, target: KeyframeValue): KeyframeValue {\n\treturn typeof target === \"number\" && typeof read === \"string\"\n\t\t? Number.parseFloat(read) || 0\n\t\t: read;\n}\n\nfunction animateChannel(\n\telement: Element,\n\tkey: string,\n\tvalue: KeyframeValue | readonly KeyframeValue[],\n\toptions: AnimationOptions,\n\tdelay: number,\n\tscripted: ScriptedChannel | undefined,\n): AnimationControls {\n\tconst channel = channelOf(element, key);\n\tconst list: readonly KeyframeValue[] = Array.isArray(value) ? value : [value];\n\tconst target = list.at(-1) as KeyframeValue;\n\tconst animations = running.get(element) ?? new Map();\n\trunning.set(element, animations);\n\tconst previous = animations.get(key);\n\tconst now = previous?.current();\n\tprevious?.stop();\n\tconst from = now?.value ?? asTarget(channel.read(), target);\n\tconst keyframes = list.length > 1 ? list : [from, target];\n\tif (\n\t\t(shouldReduceMotion(options.reducedMotion) && isMovement(key)) ||\n\t\t(!channel.native && !scripted)\n\t) {\n\t\tchannel.write(target);\n\t\treturn finishedControls();\n\t}\n\tconst reduced = shouldReduceMotion(options.reducedMotion)\n\t\t? { ...options, type: \"tween\" as const, bounce: 0 }\n\t\t: options;\n\tconst config = {\n\t\tkeyframes,\n\t\toptions: reduced,\n\t\tdelay,\n\t\tvelocity: list.length > 1 ? 0 : (now?.velocity ?? 0),\n\t};\n\tconst transform = TRANSFORMS[key];\n\tif (transform) beginTransform(element, key);\n\tconst animation = channel.native\n\t\t? waapiAnimation({\n\t\t\t\t...config,\n\t\t\t\telement,\n\t\t\t\tproperty: transform?.property ?? key,\n\t\t\t\tformat: (frame) =>\n\t\t\t\t\ttransform\n\t\t\t\t\t\t? transform.alone(withUnit(key, frame))\n\t\t\t\t\t\t: withUnit(key, frame),\n\t\t\t\tcomposite: transform ? \"add\" : \"replace\",\n\t\t\t\tcommit: channel.write,\n\t\t\t})\n\t\t: (scripted as ScriptedChannel)({ ...config, apply: channel.write });\n\tanimations.set(key, animation);\n\tanimation.finished.then(() => {\n\t\tif (animations.get(key) === animation) animations.delete(key);\n\t});\n\treturn animation;\n}\n\n/** Every key of every element, each its own animation, under one set of controls. */\nexport function animateElements(\n\telements: readonly Element[],\n\tkeyframes: DOMKeyframes,\n\toptions: AnimationOptions,\n\tscripted?: ScriptedChannel,\n): AnimationControls {\n\tconst { delay } = options;\n\treturn groupControls(\n\t\telements.flatMap((element, index) => {\n\t\t\tconst start =\n\t\t\t\ttypeof delay === \"function\"\n\t\t\t\t\t? delay(index, elements.length)\n\t\t\t\t\t: (delay ?? 0);\n\t\t\treturn Object.entries(keyframes).map(([key, value]) =>\n\t\t\t\tanimateChannel(element, key, value, options, start, scripted),\n\t\t\t);\n\t\t}),\n\t);\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { shouldReduceMotion } from \"#/accessibility/reduced-motion.ts\";\nimport { finishedControls, groupControls } from \"#/animate/controls.ts\";\nimport type { JsAnimation, JsAnimationConfig } from \"#/animate/js-animation.ts\";\nimport {\n\tbeginTransform,\n\tcommitTransform,\n\tcommittedTransform,\n\tcssName,\n\tisMovement,\n\tTRANSFORMS,\n\twithUnit,\n} from \"#/animate/style.ts\";\nimport type {\n\tAnimationControls,\n\tAnimationOptions,\n\tDOMKeyframes,\n\tKeyframeValue,\n} from \"#/animate/types.ts\";\nimport { waapiAnimation } from \"#/animate/waapi.ts\";\n\n/** Attributes an SVG element animates in JavaScript: `d` morphs, the rest are geometry. */\nconst SVG_ATTRIBUTES: Readonly<Record<string, string>> = {\n\td: \"d\",\n\tpoints: \"points\",\n\tviewBox: \"viewBox\",\n\tcx: \"cx\",\n\tcy: \"cy\",\n\tr: \"r\",\n\trx: \"rx\",\n\try: \"ry\",\n\tx1: \"x1\",\n\ty1: \"y1\",\n\tx2: \"x2\",\n\ty2: \"y2\",\n\tattrX: \"x\",\n\tattrY: \"y\",\n\tattrWidth: \"width\",\n\tattrHeight: \"height\",\n};\n\n/**\n * Plays what the browser can't (SVG attributes, CSS variables) on the frame\n * loop. Injected, so an entry that only needs the browser's half never loads\n * the interpolators.\n */\nexport type ScriptedChannel = (\n\tconfig: JsAnimationConfig<KeyframeValue>,\n) => JsAnimation<KeyframeValue>;\n\n/**\n * The independent transforms add up. The listed ones (`transform`)\n * accumulate: function by function, so their order never depends on which\n * started first.\n */\nconst COMPOSITES: Readonly<Record<string, CompositeOperation>> = {\n\ttranslate: \"add\",\n\tscale: \"add\",\n\trotate: \"add\",\n\ttransform: \"accumulate\",\n};\n\nconst running = new WeakMap<Element, Map<string, JsAnimation<KeyframeValue>>>();\n\ninterface Channel {\n\tread(): KeyframeValue;\n\twrite(value: KeyframeValue): void;\n\treadonly native: boolean;\n}\n\nfunction channelOf(element: Element, key: string): Channel {\n\tconst style = (element as HTMLElement).style;\n\tconst attribute =\n\t\telement instanceof SVGElement ? SVG_ATTRIBUTES[key] : undefined;\n\tif (TRANSFORMS[key]) {\n\t\treturn {\n\t\t\tread: () => committedTransform(element, key),\n\t\t\twrite: (value) => commitTransform(element, key, value),\n\t\t\tnative: true,\n\t\t};\n\t}\n\tif (attribute) {\n\t\treturn {\n\t\t\tread: () => element.getAttribute(attribute) ?? \"\",\n\t\t\twrite: (value) => element.setAttribute(attribute, String(value)),\n\t\t\tnative: false,\n\t\t};\n\t}\n\tconst name = cssName(key);\n\treturn {\n\t\tread: () => getComputedStyle(element).getPropertyValue(name).trim(),\n\t\twrite: (value) => style.setProperty(name, withUnit(key, value)),\n\t\t// An unregistered custom property only animates discretely in the browser.\n\t\tnative: !key.startsWith(\"--\"),\n\t};\n}\n\nfunction asTarget(read: KeyframeValue, target: KeyframeValue): KeyframeValue {\n\treturn typeof target === \"number\" && typeof read === \"string\"\n\t\t? Number.parseFloat(read) || 0\n\t\t: read;\n}\n\nfunction animateChannel(\n\telement: Element,\n\tkey: string,\n\tvalue: KeyframeValue | readonly KeyframeValue[],\n\toptions: AnimationOptions,\n\tdelay: number,\n\tscripted: ScriptedChannel | undefined,\n): AnimationControls {\n\tconst channel = channelOf(element, key);\n\tconst list: readonly KeyframeValue[] = Array.isArray(value) ? value : [value];\n\tconst target = list.at(-1) as KeyframeValue;\n\tconst animations = running.get(element) ?? new Map();\n\trunning.set(element, animations);\n\tconst previous = animations.get(key);\n\tconst now = previous?.current();\n\tprevious?.stop();\n\tconst from = now?.value ?? asTarget(channel.read(), target);\n\tconst keyframes = list.length > 1 ? list : [from, target];\n\tif (\n\t\t(shouldReduceMotion(options.reducedMotion) && isMovement(key)) ||\n\t\t(!channel.native && !scripted)\n\t) {\n\t\tchannel.write(target);\n\t\treturn finishedControls();\n\t}\n\tconst reduced = shouldReduceMotion(options.reducedMotion)\n\t\t? { ...options, type: \"tween\" as const, bounce: 0 }\n\t\t: options;\n\tconst config = {\n\t\tkeyframes,\n\t\toptions: reduced,\n\t\tdelay,\n\t\tvelocity: list.length > 1 ? 0 : (now?.velocity ?? 0),\n\t};\n\tconst transform = TRANSFORMS[key];\n\tif (transform) beginTransform(element, key);\n\tconst animation = channel.native\n\t\t? waapiAnimation({\n\t\t\t\t...config,\n\t\t\t\telement,\n\t\t\t\tproperty: transform?.property ?? key,\n\t\t\t\tformat: (frame) =>\n\t\t\t\t\ttransform\n\t\t\t\t\t\t? transform.alone(withUnit(key, frame))\n\t\t\t\t\t\t: withUnit(key, frame),\n\t\t\t\tcomposite: COMPOSITES[transform?.property ?? \"\"] ?? \"replace\",\n\t\t\t\tcommit: channel.write,\n\t\t\t})\n\t\t: (scripted as ScriptedChannel)({ ...config, apply: channel.write });\n\tanimations.set(key, animation);\n\tanimation.finished.then(() => {\n\t\tif (animations.get(key) === animation) animations.delete(key);\n\t});\n\treturn animation;\n}\n\n/** Every key of every element, each its own animation, under one set of controls. */\nexport function animateElements(\n\telements: readonly Element[],\n\tkeyframes: DOMKeyframes,\n\toptions: AnimationOptions,\n\tscripted?: ScriptedChannel,\n): AnimationControls {\n\tconst { delay } = options;\n\treturn groupControls(\n\t\telements.flatMap((element, index) => {\n\t\t\tconst start =\n\t\t\t\ttypeof delay === \"function\"\n\t\t\t\t\t? delay(index, elements.length)\n\t\t\t\t\t: (delay ?? 0);\n\t\t\treturn Object.entries(keyframes).map(([key, value]) =>\n\t\t\t\tanimateChannel(element, key, value, options, start, scripted),\n\t\t\t);\n\t\t}),\n\t);\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"style.d.ts","sourceRoot":"","sources":["../../src/animate/style.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,KAAK,iBAAiB,GAAG,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,CAAC;AAExE,UAAU,gBAAgB;IACzB,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,gFAAgF;IAChF,QAAQ,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CAC1C;AASD;;;;GAIG;AACH,eAAO,MAAM,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAkCjE,CAAC;AAiBF,2DAA2D;AAC3D,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,MAAM,CAMlE;AAkBD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAKxE;AASD,4FAA4F;AAC5F,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,QAgB/C;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,QAG3D;AAED,wBAAgB,eAAe,CAC9B,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,aAAa,QAKpB;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI3C;AAED,mGAAmG;AACnG,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE/C"}
1
+ {"version":3,"file":"style.d.ts","sourceRoot":"","sources":["../../src/animate/style.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,KAAK,iBAAiB,GAAG,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,CAAC;AAExE,UAAU,gBAAgB;IACzB,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,gFAAgF;IAChF,QAAQ,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CAC1C;AASD;;;;GAIG;AACH,eAAO,MAAM,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAqCjE,CAAC;AAiBF,2DAA2D;AAC3D,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,MAAM,CAMlE;AAkBD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAKxE;AASD,4FAA4F;AAC5F,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,QAgB/C;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,QAG3D;AAED,wBAAgB,eAAe,CAC9B,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,aAAa,QAKpB;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI3C;AAED,mGAAmG;AACnG,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE/C"}
@@ -29,7 +29,9 @@ const TRANSFORMS = {
29
29
  property: "transform",
30
30
  unit: "deg",
31
31
  identity: 0,
32
- alone: (v) => `${key}(${v})`
32
+ // The whole list, the others at identity: accumulated onto the
33
+ // inline list, every function keeps its place whatever starts when.
34
+ alone: (v) => LISTED.map((one) => `${one}(${one === key ? v : "0deg"})`).join(" ")
33
35
  }
34
36
  ])
35
37
  )
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/animate/style.ts"],
4
- "sourcesContent": ["import type { KeyframeValue } from \"#/animate/types.ts\";\n\ntype TransformProperty = \"translate\" | \"scale\" | \"rotate\" | \"transform\";\n\ninterface TransformChannel {\n\treadonly property: TransformProperty;\n\treadonly unit: string;\n\treadonly identity: number;\n\t/** The channel alone as a value of its property, the other axes at identity. */\n\treadonly alone: (value: string) => string;\n}\n\n/**\n * The transforms with no CSS property of their own (`rotate` takes one axis,\n * and skew has none): they go through `transform`, in this order, after\n * `translate`, `rotate` and `scale`.\n */\nconst LISTED = [\"rotateX\", \"rotateY\", \"skew\", \"skewX\", \"skewY\"];\n\n/**\n * The independent transforms. Each animates its CSS property alone with\n * `composite: \"add\"`, so `x` and `y` run as two compositor animations that\n * sum, and one can be interrupted without touching the other.\n */\nexport const TRANSFORMS: Readonly<Record<string, TransformChannel>> = {\n\tx: { property: \"translate\", unit: \"px\", identity: 0, alone: (v) => `${v}` },\n\ty: {\n\t\tproperty: \"translate\",\n\t\tunit: \"px\",\n\t\tidentity: 0,\n\t\talone: (v) => `0px ${v}`,\n\t},\n\tz: {\n\t\tproperty: \"translate\",\n\t\tunit: \"px\",\n\t\tidentity: 0,\n\t\talone: (v) => `0px 0px ${v}`,\n\t},\n\tscale: { property: \"scale\", unit: \"\", identity: 1, alone: (v) => `${v}` },\n\tscaleX: { property: \"scale\", unit: \"\", identity: 1, alone: (v) => `${v} 1` },\n\tscaleY: { property: \"scale\", unit: \"\", identity: 1, alone: (v) => `1 ${v}` },\n\trotate: {\n\t\tproperty: \"rotate\",\n\t\tunit: \"deg\",\n\t\tidentity: 0,\n\t\talone: (v) => `${v}`,\n\t},\n\t...Object.fromEntries(\n\t\tLISTED.map((key) => [\n\t\t\tkey,\n\t\t\t{\n\t\t\t\tproperty: \"transform\",\n\t\t\t\tunit: \"deg\",\n\t\t\t\tidentity: 0,\n\t\t\t\talone: (v: string) => `${key}(${v})`,\n\t\t\t},\n\t\t]),\n\t),\n};\n\nconst UNITLESS = new Set([\n\t\"opacity\",\n\t\"zIndex\",\n\t\"fontWeight\",\n\t\"lineHeight\",\n\t\"flexGrow\",\n\t\"flexShrink\",\n\t\"order\",\n\t\"fillOpacity\",\n\t\"strokeOpacity\",\n\t\"strokeMiterlimit\",\n\t\"aspectRatio\",\n\t\"pathLength\",\n]);\n\n/** A keyframe as CSS: numbers take the property's unit. */\nexport function withUnit(key: string, value: KeyframeValue): string {\n\tif (typeof value === \"string\") return value;\n\tconst unit =\n\t\tTRANSFORMS[key]?.unit ??\n\t\t(UNITLESS.has(key) || key.startsWith(\"--\") ? \"\" : \"px\");\n\treturn `${value}${unit}`;\n}\n\n/** Committed transform values per element, the ones no animation is driving. */\nconst committed = new WeakMap<Element, Map<string, KeyframeValue>>();\nconst animating = new WeakMap<Element, Set<string>>();\n\nfunction entries<T>(\n\tmap: WeakMap<Element, T>,\n\telement: Element,\n\tfresh: () => T,\n) {\n\tconst existing = map.get(element);\n\tif (existing) return existing;\n\tconst created = fresh();\n\tmap.set(element, created);\n\treturn created;\n}\n\nexport function committedTransform(element: Element, key: string): number {\n\tconst value = committed.get(element)?.get(key);\n\treturn typeof value === \"number\"\n\t\t? value\n\t\t: Number.parseFloat(value ?? \"\") || (TRANSFORMS[key]?.identity ?? 0);\n}\n\nfunction channel(element: Element, key: string): string {\n\tconst channelDef = TRANSFORMS[key] as TransformChannel;\n\tif (animating.get(element)?.has(key))\n\t\treturn `${channelDef.identity}${channelDef.unit}`;\n\treturn withUnit(key, committed.get(element)?.get(key) ?? channelDef.identity);\n}\n\n/** Writes the committed transforms inline, with every animating channel at its identity. */\nexport function writeTransforms(element: Element) {\n\tconst style = (element as HTMLElement).style;\n\tconst at = (key: string) => channel(element, key);\n\tstyle.translate = `${at(\"x\")} ${at(\"y\")} ${at(\"z\")}`;\n\tconst uniform = Number.parseFloat(at(\"scale\"));\n\tstyle.scale = `${uniform * Number.parseFloat(at(\"scaleX\"))} ${uniform * Number.parseFloat(at(\"scaleY\"))}`;\n\tstyle.rotate = at(\"rotate\");\n\t// `transform` stays the page's on an element that never animates these.\n\tif (\n\t\tLISTED.some(\n\t\t\t(key) =>\n\t\t\t\tcommitted.get(element)?.has(key) || animating.get(element)?.has(key),\n\t\t)\n\t) {\n\t\tstyle.transform = LISTED.map((key) => `${key}(${at(key)})`).join(\" \");\n\t}\n}\n\nexport function beginTransform(element: Element, key: string) {\n\tentries(animating, element, () => new Set()).add(key);\n\twriteTransforms(element);\n}\n\nexport function commitTransform(\n\telement: Element,\n\tkey: string,\n\tvalue: KeyframeValue,\n) {\n\tentries(committed, element, () => new Map()).set(key, value);\n\tanimating.get(element)?.delete(key);\n\twriteTransforms(element);\n}\n\nexport function cssName(key: string): string {\n\treturn key.startsWith(\"--\")\n\t\t? key\n\t\t: key.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);\n}\n\n/** Moves things on screen, as opposed to fading or tinting them: what reduced motion turns off. */\nexport function isMovement(key: string): boolean {\n\treturn !/opacity|color|fill|stroke$|filter/i.test(key);\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { KeyframeValue } from \"#/animate/types.ts\";\n\ntype TransformProperty = \"translate\" | \"scale\" | \"rotate\" | \"transform\";\n\ninterface TransformChannel {\n\treadonly property: TransformProperty;\n\treadonly unit: string;\n\treadonly identity: number;\n\t/** The channel alone as a value of its property, the other axes at identity. */\n\treadonly alone: (value: string) => string;\n}\n\n/**\n * The transforms with no CSS property of their own (`rotate` takes one axis,\n * and skew has none): they go through `transform`, in this order, after\n * `translate`, `rotate` and `scale`.\n */\nconst LISTED = [\"rotateX\", \"rotateY\", \"skew\", \"skewX\", \"skewY\"];\n\n/**\n * The independent transforms. Each animates its CSS property alone with\n * `composite: \"add\"`, so `x` and `y` run as two compositor animations that\n * sum, and one can be interrupted without touching the other.\n */\nexport const TRANSFORMS: Readonly<Record<string, TransformChannel>> = {\n\tx: { property: \"translate\", unit: \"px\", identity: 0, alone: (v) => `${v}` },\n\ty: {\n\t\tproperty: \"translate\",\n\t\tunit: \"px\",\n\t\tidentity: 0,\n\t\talone: (v) => `0px ${v}`,\n\t},\n\tz: {\n\t\tproperty: \"translate\",\n\t\tunit: \"px\",\n\t\tidentity: 0,\n\t\talone: (v) => `0px 0px ${v}`,\n\t},\n\tscale: { property: \"scale\", unit: \"\", identity: 1, alone: (v) => `${v}` },\n\tscaleX: { property: \"scale\", unit: \"\", identity: 1, alone: (v) => `${v} 1` },\n\tscaleY: { property: \"scale\", unit: \"\", identity: 1, alone: (v) => `1 ${v}` },\n\trotate: {\n\t\tproperty: \"rotate\",\n\t\tunit: \"deg\",\n\t\tidentity: 0,\n\t\talone: (v) => `${v}`,\n\t},\n\t...Object.fromEntries(\n\t\tLISTED.map((key) => [\n\t\t\tkey,\n\t\t\t{\n\t\t\t\tproperty: \"transform\",\n\t\t\t\tunit: \"deg\",\n\t\t\t\tidentity: 0,\n\t\t\t\t// The whole list, the others at identity: accumulated onto the\n\t\t\t\t// inline list, every function keeps its place whatever starts when.\n\t\t\t\talone: (v: string) =>\n\t\t\t\t\tLISTED.map((one) => `${one}(${one === key ? v : \"0deg\"})`).join(\" \"),\n\t\t\t},\n\t\t]),\n\t),\n};\n\nconst UNITLESS = new Set([\n\t\"opacity\",\n\t\"zIndex\",\n\t\"fontWeight\",\n\t\"lineHeight\",\n\t\"flexGrow\",\n\t\"flexShrink\",\n\t\"order\",\n\t\"fillOpacity\",\n\t\"strokeOpacity\",\n\t\"strokeMiterlimit\",\n\t\"aspectRatio\",\n\t\"pathLength\",\n]);\n\n/** A keyframe as CSS: numbers take the property's unit. */\nexport function withUnit(key: string, value: KeyframeValue): string {\n\tif (typeof value === \"string\") return value;\n\tconst unit =\n\t\tTRANSFORMS[key]?.unit ??\n\t\t(UNITLESS.has(key) || key.startsWith(\"--\") ? \"\" : \"px\");\n\treturn `${value}${unit}`;\n}\n\n/** Committed transform values per element, the ones no animation is driving. */\nconst committed = new WeakMap<Element, Map<string, KeyframeValue>>();\nconst animating = new WeakMap<Element, Set<string>>();\n\nfunction entries<T>(\n\tmap: WeakMap<Element, T>,\n\telement: Element,\n\tfresh: () => T,\n) {\n\tconst existing = map.get(element);\n\tif (existing) return existing;\n\tconst created = fresh();\n\tmap.set(element, created);\n\treturn created;\n}\n\nexport function committedTransform(element: Element, key: string): number {\n\tconst value = committed.get(element)?.get(key);\n\treturn typeof value === \"number\"\n\t\t? value\n\t\t: Number.parseFloat(value ?? \"\") || (TRANSFORMS[key]?.identity ?? 0);\n}\n\nfunction channel(element: Element, key: string): string {\n\tconst channelDef = TRANSFORMS[key] as TransformChannel;\n\tif (animating.get(element)?.has(key))\n\t\treturn `${channelDef.identity}${channelDef.unit}`;\n\treturn withUnit(key, committed.get(element)?.get(key) ?? channelDef.identity);\n}\n\n/** Writes the committed transforms inline, with every animating channel at its identity. */\nexport function writeTransforms(element: Element) {\n\tconst style = (element as HTMLElement).style;\n\tconst at = (key: string) => channel(element, key);\n\tstyle.translate = `${at(\"x\")} ${at(\"y\")} ${at(\"z\")}`;\n\tconst uniform = Number.parseFloat(at(\"scale\"));\n\tstyle.scale = `${uniform * Number.parseFloat(at(\"scaleX\"))} ${uniform * Number.parseFloat(at(\"scaleY\"))}`;\n\tstyle.rotate = at(\"rotate\");\n\t// `transform` stays the page's on an element that never animates these.\n\tif (\n\t\tLISTED.some(\n\t\t\t(key) =>\n\t\t\t\tcommitted.get(element)?.has(key) || animating.get(element)?.has(key),\n\t\t)\n\t) {\n\t\tstyle.transform = LISTED.map((key) => `${key}(${at(key)})`).join(\" \");\n\t}\n}\n\nexport function beginTransform(element: Element, key: string) {\n\tentries(animating, element, () => new Set()).add(key);\n\twriteTransforms(element);\n}\n\nexport function commitTransform(\n\telement: Element,\n\tkey: string,\n\tvalue: KeyframeValue,\n) {\n\tentries(committed, element, () => new Map()).set(key, value);\n\tanimating.get(element)?.delete(key);\n\twriteTransforms(element);\n}\n\nexport function cssName(key: string): string {\n\treturn key.startsWith(\"--\")\n\t\t? key\n\t\t: key.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);\n}\n\n/** Moves things on screen, as opposed to fading or tinting them: what reduced motion turns off. */\nexport function isMovement(key: string): boolean {\n\treturn !/opacity|color|fill|stroke$|filter/i.test(key);\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -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,CAkFnB"}
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"}
@@ -24,24 +24,42 @@ function plannedDuration(keyframes, options) {
24
24
  );
25
25
  }
26
26
  function animateSequence(segments, options, animateOne, resolveTargets) {
27
- const begun = now();
27
+ let rate = 1;
28
+ let base = 0;
29
+ let since = now();
30
+ let paused = false;
31
+ let halted = false;
32
+ let completing = false;
33
+ function elapsed() {
34
+ return paused ? base : base + (now() - since) / 1e3 * rate;
35
+ }
36
+ function retime(seconds) {
37
+ base = seconds;
38
+ since = now();
39
+ }
28
40
  const labels = /* @__PURE__ */ new Map();
29
41
  const busy = /* @__PURE__ */ new Map();
30
42
  const children = [];
43
+ const origins = /* @__PURE__ */ new Map();
44
+ const openers = [];
31
45
  const waits = [];
32
46
  let end = 0;
33
47
  let cursor = 0;
34
48
  let previousStart = 0;
35
49
  for (const segment of segments) {
36
- let optionsAt2 = function(elapsed) {
37
- const offset = start - elapsed;
50
+ let optionsAt2 = function(elapsed2) {
51
+ const offset = start - elapsed2;
38
52
  return {
39
53
  ...options,
40
54
  ...own,
41
55
  delay: typeof delay === "function" ? (index, total) => Math.max(0, offset + delay(index, total)) : Math.max(0, offset + (delay ?? 0))
42
56
  };
57
+ }, launch2 = function() {
58
+ const controls = animateOne(target, keyframes, optionsAt2(elapsed()));
59
+ controls.speed = rate;
60
+ return controls;
43
61
  };
44
- var optionsAt = optionsAt2;
62
+ var optionsAt = optionsAt2, launch = launch2;
45
63
  if (typeof segment === "string" || "name" in segment) {
46
64
  const label = typeof segment === "string" ? { name: segment } : segment;
47
65
  labels.set(label.name, startOf(label.at, cursor, previousStart, labels));
@@ -56,17 +74,34 @@ function animateSequence(segments, options, animateOne, resolveTargets) {
56
74
  );
57
75
  const start = Math.max(placed, ...before.map((earlier) => earlier.end));
58
76
  const { delay } = own;
59
- const launch = () => animateOne(target, keyframes, optionsAt2((now() - begun) / 1e3));
60
77
  let finished2;
61
78
  let ends;
62
79
  if (before.length === 0) {
63
- const controls = launch();
80
+ const controls = launch2();
64
81
  children.push(controls);
65
82
  finished2 = controls.finished;
66
83
  ends = Math.max(start, controls.duration);
67
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
+ });
68
103
  finished2 = Promise.all(before.map((earlier) => earlier.finished)).then(
69
- () => launch().finished
104
+ open2
70
105
  );
71
106
  waits.push(finished2);
72
107
  ends = start + plannedDuration(keyframes, { ...options, ...own });
@@ -84,8 +119,53 @@ function animateSequence(segments, options, animateOne, resolveTargets) {
84
119
  const finished = Promise.all([group.finished, ...waits]).then(
85
120
  () => void 0
86
121
  );
87
- Object.defineProperty(group, "duration", {
88
- value: Math.max(end, group.duration)
122
+ function around(action, first) {
123
+ const own = group[action];
124
+ return {
125
+ value() {
126
+ first();
127
+ own();
128
+ }
129
+ };
130
+ }
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
+ }),
159
+ // The segments still waiting count too: they end after the ones launched.
160
+ duration: { value: Math.max(end, group.duration) },
161
+ speed: {
162
+ get: () => rate,
163
+ set(next) {
164
+ retime(elapsed());
165
+ rate = next;
166
+ for (const child of children) child.speed = next;
167
+ }
168
+ }
89
169
  });
90
170
  return Object.assign(group, thenable(finished));
91
171
  }
@@ -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\tconst begun = now();\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\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\tconst launch = () =>\n\t\t\tanimateOne(target, keyframes, optionsAt((now() - begun) / 1000));\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\tfinished = Promise.all(before.map((earlier) => earlier.finished)).then(\n\t\t\t\t() => launch().finished,\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 segments still waiting count too: they end after the ones launched.\n\tObject.defineProperty(group, \"duration\", {\n\t\tvalue: Math.max(end, group.duration),\n\t});\n\treturn Object.assign(group, thenable(finished));\n}\n"],
5
- "mappings": "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",
6
- "names": ["optionsAt", "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.123",
3
+ "version": "6.0.125",
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",
@@ -47,6 +47,18 @@ export type ScriptedChannel = (
47
47
  config: JsAnimationConfig<KeyframeValue>,
48
48
  ) => JsAnimation<KeyframeValue>;
49
49
 
50
+ /**
51
+ * The independent transforms add up. The listed ones (`transform`)
52
+ * accumulate: function by function, so their order never depends on which
53
+ * started first.
54
+ */
55
+ const COMPOSITES: Readonly<Record<string, CompositeOperation>> = {
56
+ translate: "add",
57
+ scale: "add",
58
+ rotate: "add",
59
+ transform: "accumulate",
60
+ };
61
+
50
62
  const running = new WeakMap<Element, Map<string, JsAnimation<KeyframeValue>>>();
51
63
 
52
64
  interface Channel {
@@ -133,7 +145,7 @@ function animateChannel(
133
145
  transform
134
146
  ? transform.alone(withUnit(key, frame))
135
147
  : withUnit(key, frame),
136
- composite: transform ? "add" : "replace",
148
+ composite: COMPOSITES[transform?.property ?? ""] ?? "replace",
137
149
  commit: channel.write,
138
150
  })
139
151
  : (scripted as ScriptedChannel)({ ...config, apply: channel.write });
@@ -52,7 +52,10 @@ export const TRANSFORMS: Readonly<Record<string, TransformChannel>> = {
52
52
  property: "transform",
53
53
  unit: "deg",
54
54
  identity: 0,
55
- alone: (v: string) => `${key}(${v})`,
55
+ // The whole list, the others at identity: accumulated onto the
56
+ // inline list, every function keeps its place whatever starts when.
57
+ alone: (v: string) =>
58
+ LISTED.map((one) => `${one}(${one === key ? v : "0deg"})`).join(" "),
56
59
  },
57
60
  ]),
58
61
  ),
@@ -91,13 +91,32 @@ export function animateSequence(
91
91
  animateOne: AnimateOne,
92
92
  resolveTargets: ResolveTargets,
93
93
  ): AnimationControls {
94
- const begun = now();
94
+ /** The sequence's own clock: it runs at the speed the sequence is set to. */
95
+ let rate = 1;
96
+ let base = 0;
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;
102
+ function elapsed(): number {
103
+ return paused ? base : base + ((now() - since) / 1000) * rate;
104
+ }
105
+ function retime(seconds: number) {
106
+ base = seconds;
107
+ since = now();
108
+ }
95
109
  const labels = new Map<string, number>();
96
110
  const busy = new Map<
97
111
  object,
98
112
  Map<string, { readonly finished: Promise<void>; readonly end: number }>
99
113
  >();
114
+ /** Live: a segment that waited joins it once launched, and the controls reach it. */
100
115
  const children: 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)[] = [];
101
120
  const waits: Promise<void>[] = [];
102
121
  let end = 0;
103
122
  let cursor = 0;
@@ -138,8 +157,11 @@ export function animateSequence(
138
157
  : Math.max(0, offset + (delay ?? 0)),
139
158
  };
140
159
  }
141
- const launch = () =>
142
- animateOne(target, keyframes, optionsAt((now() - begun) / 1000));
160
+ function launch(): AnimationControls {
161
+ const controls = animateOne(target, keyframes, optionsAt(elapsed()));
162
+ controls.speed = rate;
163
+ return controls;
164
+ }
143
165
  let finished: Promise<void>;
144
166
  let ends: number;
145
167
  if (before.length === 0) {
@@ -148,8 +170,26 @@ export function animateSequence(
148
170
  finished = controls.finished;
149
171
  ends = Math.max(start, controls.duration);
150
172
  } else {
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
+ });
151
191
  finished = Promise.all(before.map((earlier) => earlier.finished)).then(
152
- () => launch().finished,
192
+ open,
153
193
  );
154
194
  waits.push(finished);
155
195
  ends = start + plannedDuration(keyframes, { ...options, ...own });
@@ -167,9 +207,54 @@ export function animateSequence(
167
207
  const finished = Promise.all([group.finished, ...waits]).then(
168
208
  () => undefined,
169
209
  );
170
- // The segments still waiting count too: they end after the ones launched.
171
- Object.defineProperty(group, "duration", {
172
- value: Math.max(end, group.duration),
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
+ }
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
+ }),
248
+ // The segments still waiting count too: they end after the ones launched.
249
+ duration: { value: Math.max(end, group.duration) },
250
+ speed: {
251
+ get: () => rate,
252
+ set(next: number) {
253
+ retime(elapsed());
254
+ rate = next;
255
+ for (const child of children) child.speed = next;
256
+ },
257
+ },
173
258
  });
174
259
  return Object.assign(group, thenable(finished));
175
260
  }