@voila.dev/motion 6.0.121 → 6.0.123

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -46,7 +46,10 @@ React 19 is an optional peer, used only by `/react`. The core never imports it.
46
46
  with `composite: "add"`. They run on the compositor, add up, and stop
47
47
  independently. When they finish, their values are written into the
48
48
  element's inline `translate`, `scale` and `rotate`: those three styles
49
- belong to motion on an element it animates.
49
+ belong to motion on an element it animates. `rotateX`, `rotateY`, `skew`,
50
+ `skewX` and `skewY` have no property of their own: they animate
51
+ `transform` the same way, and once one has run on an element, its inline
52
+ `transform` belongs to motion too.
50
53
  - **Interruption.** A new animation on a property starts from what is
51
54
  painted, at the speed the last one had. For numbers, motion keeps the
52
55
  generator behind each browser animation and reads both from it; for
@@ -1 +1 @@
1
- {"version":3,"file":"js-animation.d.ts","sourceRoot":"","sources":["../../src/animate/js-animation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,iBAAiB,EACjB,gBAAgB,EAEhB,MAAM,oBAAoB,CAAC;AAI5B,MAAM,WAAW,iBAAiB,CAAC,CAAC;IACnC,iFAAiF;IACjF,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,eAAe;IACf,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IACtB,UAAU,CAAC,IAAI,IAAI,CAAC;CACpB;AAED,MAAM,WAAW,WAAW,CAAC,CAAC,CAAE,SAAQ,iBAAiB;IACxD,qFAAqF;IACrF,OAAO,IAAI;QAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;QAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5D;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAuH3E"}
1
+ {"version":3,"file":"js-animation.d.ts","sourceRoot":"","sources":["../../src/animate/js-animation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,iBAAiB,EACjB,gBAAgB,EAEhB,MAAM,oBAAoB,CAAC;AAI5B,MAAM,WAAW,iBAAiB,CAAC,CAAC;IACnC,iFAAiF;IACjF,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,eAAe;IACf,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IACtB,UAAU,CAAC,IAAI,IAAI,CAAC;CACpB;AAED,MAAM,WAAW,WAAW,CAAC,CAAC,CAAE,SAAQ,iBAAiB;IACxD,qFAAqF;IACrF,OAAO,IAAI;QAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;QAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5D;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CA2H3E"}
@@ -44,17 +44,23 @@ function jsAnimation(config) {
44
44
  cancelFrame(tick);
45
45
  resolve();
46
46
  }
47
+ function finish() {
48
+ time = speed < 0 ? 0 : duration;
49
+ apply(speed < 0 ? keyframes[0] : last);
50
+ settle("finished");
51
+ config.onComplete?.();
52
+ }
47
53
  function tick(data) {
48
- time = Math.max(0, time + (data.timestamp - previous) / 1e3 * speed);
54
+ time = Math.min(
55
+ duration,
56
+ Math.max(0, time + (data.timestamp - previous) / 1e3 * speed)
57
+ );
49
58
  previous = data.timestamp;
50
- if (time < duration) {
59
+ if (speed < 0 ? time > 0 : time < duration) {
51
60
  render();
52
61
  return;
53
62
  }
54
- time = duration;
55
- render();
56
- settle("finished");
57
- config.onComplete?.();
63
+ finish();
58
64
  }
59
65
  function play() {
60
66
  if (state === "running") return;
@@ -95,11 +101,7 @@ function jsAnimation(config) {
95
101
  settle("finished");
96
102
  },
97
103
  complete() {
98
- if (state === "finished") return;
99
- time = duration;
100
- render();
101
- settle("finished");
102
- config.onComplete?.();
104
+ if (state !== "finished") finish();
103
105
  },
104
106
  attachTimeline: () => false,
105
107
  current() {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/animate/js-animation.ts"],
4
- "sourcesContent": ["import { thenable } from \"#/animate/controls.ts\";\nimport { iterationTime, planGenerator, totalDuration } from \"#/animate/plan.ts\";\nimport type {\n\tAnimationControls,\n\tAnimationOptions,\n\tAnimationState,\n} from \"#/animate/types.ts\";\nimport { cancelFrame, type FrameData, frame, now } from \"#/frame/frame.ts\";\nimport { mix } from \"#/interpolate/mix.ts\";\n\nexport interface JsAnimationConfig<T> {\n\t/** At least two: the caller resolves a lone target against the current value. */\n\treadonly keyframes: readonly T[];\n\treadonly options: AnimationOptions;\n\t/** Seconds. */\n\treadonly delay: number;\n\t/** The velocity the value has now, carried into a spring. */\n\treadonly velocity: number;\n\tapply(value: T): void;\n\tonComplete?(): void;\n}\n\nexport interface JsAnimation<T> extends AnimationControls {\n\t/** The value and velocity at the current time: where an interruption starts from. */\n\tcurrent(): { readonly value: T; readonly velocity: number };\n}\n\n/**\n * An animation stepped by the frame loop: plain values, objects, SVG\n * attributes and CSS variables \u2014 whatever the Web Animations API can't play.\n */\nexport function jsAnimation<T>(config: JsAnimationConfig<T>): JsAnimation<T> {\n\tconst { keyframes, options, delay, apply } = config;\n\tconst { generator, positional } = planGenerator(\n\t\tkeyframes,\n\t\toptions,\n\t\tconfig.velocity,\n\t);\n\tconst segments = positional\n\t\t? keyframes.slice(1).map((to, index) => mix(keyframes[index] as T, to as T))\n\t\t: [];\n\tconst active = totalDuration(generator.duration, options);\n\tconst duration = delay + active;\n\tconst endsReversed =\n\t\toptions.repeatType === \"reverse\" && (options.repeat ?? 0) % 2 === 1;\n\tconst last = keyframes[endsReversed ? 0 : keyframes.length - 1] as T;\n\n\tlet time = 0;\n\tlet speed = 1;\n\tlet state: AnimationState = \"running\";\n\tlet previous = now();\n\tlet resolve: () => void = () => {};\n\tconst finished = new Promise<void>((done) => {\n\t\tresolve = done;\n\t});\n\n\tfunction valueAt(position: number): T {\n\t\tif (!positional) return position as T;\n\t\tconst index = Math.min(\n\t\t\tMath.max(Math.floor(position), 0),\n\t\t\tsegments.length - 1,\n\t\t);\n\t\treturn (segments[index] as (progress: number) => T)(position - index);\n\t}\n\n\tfunction sample() {\n\t\tconst elapsed = Math.max(0, time - delay);\n\t\treturn generator.at(iterationTime(elapsed, generator.duration, options));\n\t}\n\n\tfunction render() {\n\t\tif (time < delay) return;\n\t\tapply(time >= duration ? last : valueAt(sample().value));\n\t}\n\n\tfunction settle(next: AnimationState) {\n\t\tstate = next;\n\t\tcancelFrame(tick);\n\t\tresolve();\n\t}\n\n\tfunction tick(data: FrameData) {\n\t\ttime = Math.max(0, time + ((data.timestamp - previous) / 1000) * speed);\n\t\tprevious = data.timestamp;\n\t\tif (time < duration) {\n\t\t\trender();\n\t\t\treturn;\n\t\t}\n\t\ttime = duration;\n\t\trender();\n\t\tsettle(\"finished\");\n\t\tconfig.onComplete?.();\n\t}\n\n\tfunction play() {\n\t\tif (state === \"running\") return;\n\t\tprevious = now();\n\t\tstate = \"running\";\n\t\tframe.update(tick, true);\n\t}\n\n\tframe.update(tick, true);\n\trender();\n\n\treturn {\n\t\t...thenable(finished),\n\t\tget time() {\n\t\t\treturn time;\n\t\t},\n\t\tset time(seconds) {\n\t\t\ttime = Math.min(Math.max(seconds, 0), duration);\n\t\t\trender();\n\t\t},\n\t\tget speed() {\n\t\t\treturn speed;\n\t\t},\n\t\tset speed(rate) {\n\t\t\tspeed = rate;\n\t\t},\n\t\tduration,\n\t\tget state() {\n\t\t\treturn state;\n\t\t},\n\t\tplay,\n\t\tpause() {\n\t\t\tif (state !== \"running\") return;\n\t\t\tstate = \"paused\";\n\t\t\tcancelFrame(tick);\n\t\t},\n\t\tstop: () => settle(\"finished\"),\n\t\tcancel() {\n\t\t\tapply(keyframes[0] as T);\n\t\t\tsettle(\"finished\");\n\t\t},\n\t\tcomplete() {\n\t\t\tif (state === \"finished\") return;\n\t\t\ttime = duration;\n\t\t\trender();\n\t\t\tsettle(\"finished\");\n\t\t\tconfig.onComplete?.();\n\t\t},\n\t\tattachTimeline: () => false,\n\t\tcurrent() {\n\t\t\tconst { value, velocity } = sample();\n\t\t\treturn {\n\t\t\t\tvalue: time < delay ? (keyframes[0] as T) : valueAt(value),\n\t\t\t\tvelocity: positional || time < delay ? 0 : velocity,\n\t\t\t};\n\t\t},\n\t};\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { thenable } from \"#/animate/controls.ts\";\nimport { iterationTime, planGenerator, totalDuration } from \"#/animate/plan.ts\";\nimport type {\n\tAnimationControls,\n\tAnimationOptions,\n\tAnimationState,\n} from \"#/animate/types.ts\";\nimport { cancelFrame, type FrameData, frame, now } from \"#/frame/frame.ts\";\nimport { mix } from \"#/interpolate/mix.ts\";\n\nexport interface JsAnimationConfig<T> {\n\t/** At least two: the caller resolves a lone target against the current value. */\n\treadonly keyframes: readonly T[];\n\treadonly options: AnimationOptions;\n\t/** Seconds. */\n\treadonly delay: number;\n\t/** The velocity the value has now, carried into a spring. */\n\treadonly velocity: number;\n\tapply(value: T): void;\n\tonComplete?(): void;\n}\n\nexport interface JsAnimation<T> extends AnimationControls {\n\t/** The value and velocity at the current time: where an interruption starts from. */\n\tcurrent(): { readonly value: T; readonly velocity: number };\n}\n\n/**\n * An animation stepped by the frame loop: plain values, objects, SVG\n * attributes and CSS variables \u2014 whatever the Web Animations API can't play.\n */\nexport function jsAnimation<T>(config: JsAnimationConfig<T>): JsAnimation<T> {\n\tconst { keyframes, options, delay, apply } = config;\n\tconst { generator, positional } = planGenerator(\n\t\tkeyframes,\n\t\toptions,\n\t\tconfig.velocity,\n\t);\n\tconst segments = positional\n\t\t? keyframes.slice(1).map((to, index) => mix(keyframes[index] as T, to as T))\n\t\t: [];\n\tconst active = totalDuration(generator.duration, options);\n\tconst duration = delay + active;\n\tconst endsReversed =\n\t\toptions.repeatType === \"reverse\" && (options.repeat ?? 0) % 2 === 1;\n\tconst last = keyframes[endsReversed ? 0 : keyframes.length - 1] as T;\n\n\tlet time = 0;\n\tlet speed = 1;\n\tlet state: AnimationState = \"running\";\n\tlet previous = now();\n\tlet resolve: () => void = () => {};\n\tconst finished = new Promise<void>((done) => {\n\t\tresolve = done;\n\t});\n\n\tfunction valueAt(position: number): T {\n\t\tif (!positional) return position as T;\n\t\tconst index = Math.min(\n\t\t\tMath.max(Math.floor(position), 0),\n\t\t\tsegments.length - 1,\n\t\t);\n\t\treturn (segments[index] as (progress: number) => T)(position - index);\n\t}\n\n\tfunction sample() {\n\t\tconst elapsed = Math.max(0, time - delay);\n\t\treturn generator.at(iterationTime(elapsed, generator.duration, options));\n\t}\n\n\tfunction render() {\n\t\tif (time < delay) return;\n\t\tapply(time >= duration ? last : valueAt(sample().value));\n\t}\n\n\tfunction settle(next: AnimationState) {\n\t\tstate = next;\n\t\tcancelFrame(tick);\n\t\tresolve();\n\t}\n\n\t/** Played backwards, it ends where it started, the way WAAPI does. */\n\tfunction finish() {\n\t\ttime = speed < 0 ? 0 : duration;\n\t\tapply(speed < 0 ? (keyframes[0] as T) : last);\n\t\tsettle(\"finished\");\n\t\tconfig.onComplete?.();\n\t}\n\n\tfunction tick(data: FrameData) {\n\t\ttime = Math.min(\n\t\t\tduration,\n\t\t\tMath.max(0, time + ((data.timestamp - previous) / 1000) * speed),\n\t\t);\n\t\tprevious = data.timestamp;\n\t\tif (speed < 0 ? time > 0 : time < duration) {\n\t\t\trender();\n\t\t\treturn;\n\t\t}\n\t\tfinish();\n\t}\n\n\tfunction play() {\n\t\tif (state === \"running\") return;\n\t\tprevious = now();\n\t\tstate = \"running\";\n\t\tframe.update(tick, true);\n\t}\n\n\tframe.update(tick, true);\n\trender();\n\n\treturn {\n\t\t...thenable(finished),\n\t\tget time() {\n\t\t\treturn time;\n\t\t},\n\t\tset time(seconds) {\n\t\t\ttime = Math.min(Math.max(seconds, 0), duration);\n\t\t\trender();\n\t\t},\n\t\tget speed() {\n\t\t\treturn speed;\n\t\t},\n\t\tset speed(rate) {\n\t\t\tspeed = rate;\n\t\t},\n\t\tduration,\n\t\tget state() {\n\t\t\treturn state;\n\t\t},\n\t\tplay,\n\t\tpause() {\n\t\t\tif (state !== \"running\") return;\n\t\t\tstate = \"paused\";\n\t\t\tcancelFrame(tick);\n\t\t},\n\t\tstop: () => settle(\"finished\"),\n\t\tcancel() {\n\t\t\tapply(keyframes[0] as T);\n\t\t\tsettle(\"finished\");\n\t\t},\n\t\tcomplete() {\n\t\t\tif (state !== \"finished\") finish();\n\t\t},\n\t\tattachTimeline: () => false,\n\t\tcurrent() {\n\t\t\tconst { value, velocity } = sample();\n\t\t\treturn {\n\t\t\t\tvalue: time < delay ? (keyframes[0] as T) : valueAt(value),\n\t\t\t\tvelocity: positional || time < delay ? 0 : velocity,\n\t\t\t};\n\t\t},\n\t};\n}\n"],
5
+ "mappings": "AAAA,SAAS,gBAAgB;AACzB,SAAS,eAAe,eAAe,qBAAqB;AAM5D,SAAS,aAA6B,OAAO,WAAW;AACxD,SAAS,WAAW;AAuBb,SAAS,YAAe,QAA8C;AAC5E,QAAM,EAAE,WAAW,SAAS,OAAO,MAAM,IAAI;AAC7C,QAAM,EAAE,WAAW,WAAW,IAAI;AAAA,IACjC;AAAA,IACA;AAAA,IACA,OAAO;AAAA,EACR;AACA,QAAM,WAAW,aACd,UAAU,MAAM,CAAC,EAAE,IAAI,CAAC,IAAI,UAAU,IAAI,UAAU,KAAK,GAAQ,EAAO,CAAC,IACzE,CAAC;AACJ,QAAM,SAAS,cAAc,UAAU,UAAU,OAAO;AACxD,QAAM,WAAW,QAAQ;AACzB,QAAM,eACL,QAAQ,eAAe,cAAc,QAAQ,UAAU,KAAK,MAAM;AACnE,QAAM,OAAO,UAAU,eAAe,IAAI,UAAU,SAAS,CAAC;AAE9D,MAAI,OAAO;AACX,MAAI,QAAQ;AACZ,MAAI,QAAwB;AAC5B,MAAI,WAAW,IAAI;AACnB,MAAI,UAAsB,MAAM;AAAA,EAAC;AACjC,QAAM,WAAW,IAAI,QAAc,CAAC,SAAS;AAC5C,cAAU;AAAA,EACX,CAAC;AAED,WAAS,QAAQ,UAAqB;AACrC,QAAI,CAAC,WAAY,QAAO;AACxB,UAAM,QAAQ,KAAK;AAAA,MAClB,KAAK,IAAI,KAAK,MAAM,QAAQ,GAAG,CAAC;AAAA,MAChC,SAAS,SAAS;AAAA,IACnB;AACA,WAAQ,SAAS,KAAK,EAA8B,WAAW,KAAK;AAAA,EACrE;AAEA,WAAS,SAAS;AACjB,UAAM,UAAU,KAAK,IAAI,GAAG,OAAO,KAAK;AACxC,WAAO,UAAU,GAAG,cAAc,SAAS,UAAU,UAAU,OAAO,CAAC;AAAA,EACxE;AAEA,WAAS,SAAS;AACjB,QAAI,OAAO,MAAO;AAClB,UAAM,QAAQ,WAAW,OAAO,QAAQ,OAAO,EAAE,KAAK,CAAC;AAAA,EACxD;AAEA,WAAS,OAAO,MAAsB;AACrC,YAAQ;AACR,gBAAY,IAAI;AAChB,YAAQ;AAAA,EACT;AAGA,WAAS,SAAS;AACjB,WAAO,QAAQ,IAAI,IAAI;AACvB,UAAM,QAAQ,IAAK,UAAU,CAAC,IAAU,IAAI;AAC5C,WAAO,UAAU;AACjB,WAAO,aAAa;AAAA,EACrB;AAEA,WAAS,KAAK,MAAiB;AAC9B,WAAO,KAAK;AAAA,MACX;AAAA,MACA,KAAK,IAAI,GAAG,QAAS,KAAK,YAAY,YAAY,MAAQ,KAAK;AAAA,IAChE;AACA,eAAW,KAAK;AAChB,QAAI,QAAQ,IAAI,OAAO,IAAI,OAAO,UAAU;AAC3C,aAAO;AACP;AAAA,IACD;AACA,WAAO;AAAA,EACR;AAEA,WAAS,OAAO;AACf,QAAI,UAAU,UAAW;AACzB,eAAW,IAAI;AACf,YAAQ;AACR,UAAM,OAAO,MAAM,IAAI;AAAA,EACxB;AAEA,QAAM,OAAO,MAAM,IAAI;AACvB,SAAO;AAEP,SAAO;AAAA,IACN,GAAG,SAAS,QAAQ;AAAA,IACpB,IAAI,OAAO;AACV,aAAO;AAAA,IACR;AAAA,IACA,IAAI,KAAK,SAAS;AACjB,aAAO,KAAK,IAAI,KAAK,IAAI,SAAS,CAAC,GAAG,QAAQ;AAC9C,aAAO;AAAA,IACR;AAAA,IACA,IAAI,QAAQ;AACX,aAAO;AAAA,IACR;AAAA,IACA,IAAI,MAAM,MAAM;AACf,cAAQ;AAAA,IACT;AAAA,IACA;AAAA,IACA,IAAI,QAAQ;AACX,aAAO;AAAA,IACR;AAAA,IACA;AAAA,IACA,QAAQ;AACP,UAAI,UAAU,UAAW;AACzB,cAAQ;AACR,kBAAY,IAAI;AAAA,IACjB;AAAA,IACA,MAAM,MAAM,OAAO,UAAU;AAAA,IAC7B,SAAS;AACR,YAAM,UAAU,CAAC,CAAM;AACvB,aAAO,UAAU;AAAA,IAClB;AAAA,IACA,WAAW;AACV,UAAI,UAAU,WAAY,QAAO;AAAA,IAClC;AAAA,IACA,gBAAgB,MAAM;AAAA,IACtB,UAAU;AACT,YAAM,EAAE,OAAO,SAAS,IAAI,OAAO;AACnC,aAAO;AAAA,QACN,OAAO,OAAO,QAAS,UAAU,CAAC,IAAU,QAAQ,KAAK;AAAA,QACzD,UAAU,cAAc,OAAO,QAAQ,IAAI;AAAA,MAC5C;AAAA,IACD;AAAA,EACD;AACD;",
6
6
  "names": []
7
7
  }
@@ -1,5 +1,5 @@
1
1
  import type { KeyframeValue } from "./types.js";
2
- type TransformProperty = "translate" | "scale" | "rotate";
2
+ type TransformProperty = "translate" | "scale" | "rotate" | "transform";
3
3
  interface TransformChannel {
4
4
  readonly property: TransformProperty;
5
5
  readonly unit: string;
@@ -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,CAAC;AAE1D,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;AAED;;;;GAIG;AACH,eAAO,MAAM,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAuBjE,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,QAO/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,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,3 +1,4 @@
1
+ const LISTED = ["rotateX", "rotateY", "skew", "skewX", "skewY"];
1
2
  const TRANSFORMS = {
2
3
  x: { property: "translate", unit: "px", identity: 0, alone: (v) => `${v}` },
3
4
  y: {
@@ -20,7 +21,18 @@ const TRANSFORMS = {
20
21
  unit: "deg",
21
22
  identity: 0,
22
23
  alone: (v) => `${v}`
23
- }
24
+ },
25
+ ...Object.fromEntries(
26
+ LISTED.map((key) => [
27
+ key,
28
+ {
29
+ property: "transform",
30
+ unit: "deg",
31
+ identity: 0,
32
+ alone: (v) => `${key}(${v})`
33
+ }
34
+ ])
35
+ )
24
36
  };
25
37
  const UNITLESS = /* @__PURE__ */ new Set([
26
38
  "opacity",
@@ -67,6 +79,11 @@ function writeTransforms(element) {
67
79
  const uniform = Number.parseFloat(at("scale"));
68
80
  style.scale = `${uniform * Number.parseFloat(at("scaleX"))} ${uniform * Number.parseFloat(at("scaleY"))}`;
69
81
  style.rotate = at("rotate");
82
+ if (LISTED.some(
83
+ (key) => committed.get(element)?.has(key) || animating.get(element)?.has(key)
84
+ )) {
85
+ style.transform = LISTED.map((key) => `${key}(${at(key)})`).join(" ");
86
+ }
70
87
  }
71
88
  function beginTransform(element, key) {
72
89
  entries(animating, element, () => /* @__PURE__ */ new Set()).add(key);
@@ -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\";\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 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};\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}\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\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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",
6
6
  "names": []
7
7
  }
@@ -52,8 +52,8 @@ export type KeyframeValue = number | string;
52
52
  export type Keyframes<T = KeyframeValue> = T | readonly T[];
53
53
  /**
54
54
  * DOM keyframes: any CSS property (camelCase), a CSS variable (`--name`),
55
- * the independent transforms (`x`, `y`, `z`, `scale`, `scaleX`, `scaleY`,
56
- * `rotate`), or an SVG attribute. A single value animates from the current
55
+ * the transforms (`x`, `y`, `z`, `scale`, `scaleX`, `scaleY`,
56
+ * `rotate`, `rotateX`, `rotateY`, `skew`, `skewX`, `skewY`), or an SVG attribute. A single value animates from the current
57
57
  * one; an array lists every keyframe. Numbers take the property's unit (px,
58
58
  * deg) when it has one.
59
59
  */
@@ -1 +1 @@
1
- {"version":3,"file":"waapi.d.ts","sourceRoot":"","sources":["../../src/animate/waapi.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAG7D,OAAO,KAAK,EACX,gBAAgB,EAEhB,aAAa,EACb,MAAM,oBAAoB,CAAC;AAM5B,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,6CAA6C;IAC7C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,SAAS,aAAa,EAAE,CAAC;IAC7C,8CAA8C;IAC9C,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;IACvC,8DAA8D;IAC9D,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IACnC,UAAU,CAAC,IAAI,IAAI,CAAC;CACpB;AA0ED,yEAAyE;AACzE,wBAAgB,cAAc,CAC7B,MAAM,EAAE,WAAW,GACjB,WAAW,CAAC,aAAa,CAAC,CA0G5B"}
1
+ {"version":3,"file":"waapi.d.ts","sourceRoot":"","sources":["../../src/animate/waapi.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAG7D,OAAO,KAAK,EACX,gBAAgB,EAEhB,aAAa,EACb,MAAM,oBAAoB,CAAC;AAM5B,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,6CAA6C;IAC7C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,SAAS,aAAa,EAAE,CAAC;IAC7C,8CAA8C;IAC9C,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;IACvC,8DAA8D;IAC9D,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IACnC,UAAU,CAAC,IAAI,IAAI,CAAC;CACpB;AA0ED,yEAAyE;AACzE,wBAAgB,cAAc,CAC7B,MAAM,EAAE,WAAW,GACjB,WAAW,CAAC,aAAa,CAAC,CA+G5B"}
@@ -78,6 +78,9 @@ function waapiAnimation(config) {
78
78
  composite: config.composite
79
79
  });
80
80
  const last = keyframes[options.repeatType === "reverse" && iterations % 2 === 0 ? 0 : keyframes.length - 1];
81
+ function end() {
82
+ return animation.playbackRate < 0 ? keyframes[0] : last;
83
+ }
81
84
  let settled = false;
82
85
  let held = keyframes[0];
83
86
  let resolve = () => {
@@ -110,7 +113,7 @@ function waapiAnimation(config) {
110
113
  animation.finished.then(
111
114
  () => {
112
115
  if (settled) return;
113
- settle(last);
116
+ settle(end());
114
117
  config.onComplete?.();
115
118
  },
116
119
  () => settle(void 0)
@@ -138,7 +141,7 @@ function waapiAnimation(config) {
138
141
  stop: () => settle(current().value),
139
142
  cancel: () => settle(keyframes[0]),
140
143
  // The browser refuses to finish an endless animation: settle on its last frame instead.
141
- complete: () => iterations === Number.POSITIVE_INFINITY ? settle(last) : animation.finish(),
144
+ complete: () => iterations === Number.POSITIVE_INFINITY ? settle(end()) : animation.finish(),
142
145
  attachTimeline(timeline) {
143
146
  animation.timeline = timeline;
144
147
  return true;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/animate/waapi.ts"],
4
- "sourcesContent": ["import { thenable } from \"#/animate/controls.ts\";\nimport type { JsAnimation } from \"#/animate/js-animation.ts\";\nimport { iterationTime, planGenerator } from \"#/animate/plan.ts\";\nimport { cssName } from \"#/animate/style.ts\";\nimport type {\n\tAnimationOptions,\n\tAnimationState,\n\tKeyframeValue,\n} from \"#/animate/types.ts\";\nimport { linearEasing, supportsLinearEasing } from \"#/easing/linear-css.ts\";\nimport { easingToCss, resolveEasing } from \"#/easing/named.ts\";\nimport type { EasingDefinition } from \"#/easing/types.ts\";\nimport type { Generator } from \"#/generators/types.ts\";\n\nexport interface WaapiConfig {\n\treadonly element: Element;\n\t/** The CSS property the browser animates. */\n\treadonly property: string;\n\treadonly keyframes: readonly KeyframeValue[];\n\t/** A keyframe as the property's CSS value. */\n\tformat(value: KeyframeValue): string;\n\treadonly options: AnimationOptions;\n\treadonly delay: number;\n\treadonly velocity: number;\n\treadonly composite: CompositeOperation;\n\t/** Writes the value that stays once the animation is gone. */\n\tcommit(value: KeyframeValue): void;\n\tonComplete?(): void;\n}\n\nconst SAMPLE_STEP = 1 / 60;\n\nfunction cssEasing(definition: EasingDefinition, duration: number): string {\n\treturn (\n\t\teasingToCss(definition) ?? linearEasing(resolveEasing(definition), duration)\n\t);\n}\n\n/**\n * The browser keyframes and easing. Two keyframes on a spring become one\n * `linear()` easing sampled from the spring, so the compositor plays the\n * exact curve. Without `linear()`, or when the spring starts and ends at the\n * same value (a flick of velocity), the spring is sampled into keyframes.\n */\nfunction browserKeyframes(\n\tconfig: WaapiConfig,\n\tgenerator: Generator,\n\tpositional: boolean,\n): { keyframes: Keyframe[]; easing: string } {\n\tconst { keyframes, format, property, options } = config;\n\tconst duration = generator.duration;\n\tconst [from, to] = keyframes;\n\tconst springs =\n\t\toptions.type !== \"tween\" && keyframes.length === 2 && !options.ease;\n\tconst span =\n\t\ttypeof from === \"number\" && typeof to === \"number\" ? to - from : 1;\n\tif (springs && span !== 0 && supportsLinearEasing()) {\n\t\tconst progress = (t: number) => {\n\t\t\tconst { value } = generator.at(t * duration);\n\t\t\treturn positional ? value : (value - (from as number)) / span;\n\t\t};\n\t\treturn {\n\t\t\tkeyframes: keyframes.map((frame) => ({ [property]: format(frame) })),\n\t\t\teasing: linearEasing(progress, duration),\n\t\t};\n\t}\n\tif (springs && !positional) {\n\t\tconst count = Math.max(2, Math.ceil(duration / SAMPLE_STEP) + 1);\n\t\treturn {\n\t\t\tkeyframes: Array.from({ length: count }, (_unused, index) => {\n\t\t\t\tconst offset = index / (count - 1);\n\t\t\t\treturn {\n\t\t\t\t\t[property]: format(generator.at(offset * duration).value),\n\t\t\t\t\toffset,\n\t\t\t\t};\n\t\t\t}),\n\t\t\teasing: \"linear\",\n\t\t};\n\t}\n\tconst ease = options.ease ?? \"easeOut\";\n\treturn {\n\t\tkeyframes: keyframes.map((frame, index) => ({\n\t\t\t[property]: format(frame),\n\t\t\toffset: options.times?.[index] ?? index / (keyframes.length - 1),\n\t\t\teasing: cssEasing(\n\t\t\t\tArray.isArray(ease) && typeof ease[0] !== \"number\"\n\t\t\t\t\t? ((ease as readonly EasingDefinition[])[index] ?? \"linear\")\n\t\t\t\t\t: (ease as EasingDefinition),\n\t\t\t\tduration,\n\t\t\t),\n\t\t})),\n\t\teasing: \"linear\",\n\t};\n}\n\nconst STATES: Record<AnimationPlayState, AnimationState> = {\n\trunning: \"running\",\n\tpaused: \"paused\",\n\tfinished: \"finished\",\n\tidle: \"finished\",\n};\n\n/** One CSS property of one element, played by the Web Animations API. */\nexport function waapiAnimation(\n\tconfig: WaapiConfig,\n): JsAnimation<KeyframeValue> {\n\tconst { element, keyframes, options, delay, commit } = config;\n\tconst { generator, positional } = planGenerator(\n\t\tkeyframes,\n\t\toptions,\n\t\tconfig.velocity,\n\t);\n\tconst iterations = (options.repeat ?? 0) + 1;\n\tconst { keyframes: frames, easing } = browserKeyframes(\n\t\tconfig,\n\t\tgenerator,\n\t\tpositional,\n\t);\n\tconst animation = element.animate(frames, {\n\t\tduration: generator.duration * 1000,\n\t\tdelay: delay * 1000,\n\t\titerations,\n\t\tdirection: options.repeatType === \"reverse\" ? \"alternate\" : \"normal\",\n\t\teasing,\n\t\tfill: \"both\",\n\t\tcomposite: config.composite,\n\t});\n\tconst last = keyframes[\n\t\toptions.repeatType === \"reverse\" && iterations % 2 === 0\n\t\t\t? 0\n\t\t\t: keyframes.length - 1\n\t] as KeyframeValue;\n\n\tlet settled = false;\n\t/** What stayed on screen when it settled: where a following animation starts. */\n\tlet held: KeyframeValue = keyframes[0] as KeyframeValue;\n\tlet resolve: () => void = () => {};\n\tconst finished = new Promise<void>((done) => {\n\t\tresolve = done;\n\t});\n\n\tfunction current() {\n\t\tif (settled) return { value: held, velocity: 0 };\n\t\tconst elapsed = (Number(animation.currentTime) || 0) / 1000 - delay;\n\t\tif (elapsed <= 0)\n\t\t\treturn { value: keyframes[0] as KeyframeValue, velocity: 0 };\n\t\tconst state = generator.at(\n\t\t\titerationTime(elapsed, generator.duration, options),\n\t\t);\n\t\tif (!positional) return state;\n\t\t// The browser mixes colours and strings itself: ask it what it painted.\n\t\tconst painted =\n\t\t\tconfig.composite === \"replace\"\n\t\t\t\t? getComputedStyle(element).getPropertyValue(cssName(config.property))\n\t\t\t\t: \"\";\n\t\treturn { value: painted || last, velocity: 0 };\n\t}\n\n\tfunction settle(value: KeyframeValue | undefined) {\n\t\tif (settled) return;\n\t\tsettled = true;\n\t\tif (value !== undefined) {\n\t\t\theld = value;\n\t\t\tcommit(value);\n\t\t}\n\t\tanimation.cancel();\n\t\tresolve();\n\t}\n\n\tanimation.finished.then(\n\t\t() => {\n\t\t\tif (settled) return;\n\t\t\tsettle(last);\n\t\t\tconfig.onComplete?.();\n\t\t},\n\t\t() => settle(undefined),\n\t);\n\n\treturn {\n\t\t...thenable(finished),\n\t\tget time() {\n\t\t\treturn (Number(animation.currentTime) || 0) / 1000;\n\t\t},\n\t\tset time(seconds) {\n\t\t\tanimation.currentTime = seconds * 1000;\n\t\t},\n\t\tget speed() {\n\t\t\treturn animation.playbackRate;\n\t\t},\n\t\tset speed(rate) {\n\t\t\tanimation.playbackRate = rate;\n\t\t},\n\t\tduration: delay + generator.duration * iterations,\n\t\tget state() {\n\t\t\treturn settled ? \"finished\" : STATES[animation.playState];\n\t\t},\n\t\tplay: () => animation.play(),\n\t\tpause: () => animation.pause(),\n\t\tstop: () => settle(current().value),\n\t\tcancel: () => settle(keyframes[0]),\n\t\t// The browser refuses to finish an endless animation: settle on its last frame instead.\n\t\tcomplete: () =>\n\t\t\titerations === Number.POSITIVE_INFINITY\n\t\t\t\t? settle(last)\n\t\t\t\t: animation.finish(),\n\t\tattachTimeline(timeline) {\n\t\t\tanimation.timeline = timeline;\n\t\t\treturn true;\n\t\t},\n\t\tcurrent,\n\t};\n}\n"],
5
- "mappings": "AAAA,SAAS,gBAAgB;AAEzB,SAAS,eAAe,qBAAqB;AAC7C,SAAS,eAAe;AAMxB,SAAS,cAAc,4BAA4B;AACnD,SAAS,aAAa,qBAAqB;AAoB3C,MAAM,cAAc,IAAI;AAExB,SAAS,UAAU,YAA8B,UAA0B;AAC1E,SACC,YAAY,UAAU,KAAK,aAAa,cAAc,UAAU,GAAG,QAAQ;AAE7E;AAQA,SAAS,iBACR,QACA,WACA,YAC4C;AAC5C,QAAM,EAAE,WAAW,QAAQ,UAAU,QAAQ,IAAI;AACjD,QAAM,WAAW,UAAU;AAC3B,QAAM,CAAC,MAAM,EAAE,IAAI;AACnB,QAAM,UACL,QAAQ,SAAS,WAAW,UAAU,WAAW,KAAK,CAAC,QAAQ;AAChE,QAAM,OACL,OAAO,SAAS,YAAY,OAAO,OAAO,WAAW,KAAK,OAAO;AAClE,MAAI,WAAW,SAAS,KAAK,qBAAqB,GAAG;AACpD,UAAM,WAAW,CAAC,MAAc;AAC/B,YAAM,EAAE,MAAM,IAAI,UAAU,GAAG,IAAI,QAAQ;AAC3C,aAAO,aAAa,SAAS,QAAS,QAAmB;AAAA,IAC1D;AACA,WAAO;AAAA,MACN,WAAW,UAAU,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,GAAG,OAAO,KAAK,EAAE,EAAE;AAAA,MACnE,QAAQ,aAAa,UAAU,QAAQ;AAAA,IACxC;AAAA,EACD;AACA,MAAI,WAAW,CAAC,YAAY;AAC3B,UAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,KAAK,WAAW,WAAW,IAAI,CAAC;AAC/D,WAAO;AAAA,MACN,WAAW,MAAM,KAAK,EAAE,QAAQ,MAAM,GAAG,CAAC,SAAS,UAAU;AAC5D,cAAM,SAAS,SAAS,QAAQ;AAChC,eAAO;AAAA,UACN,CAAC,QAAQ,GAAG,OAAO,UAAU,GAAG,SAAS,QAAQ,EAAE,KAAK;AAAA,UACxD;AAAA,QACD;AAAA,MACD,CAAC;AAAA,MACD,QAAQ;AAAA,IACT;AAAA,EACD;AACA,QAAM,OAAO,QAAQ,QAAQ;AAC7B,SAAO;AAAA,IACN,WAAW,UAAU,IAAI,CAAC,OAAO,WAAW;AAAA,MAC3C,CAAC,QAAQ,GAAG,OAAO,KAAK;AAAA,MACxB,QAAQ,QAAQ,QAAQ,KAAK,KAAK,SAAS,UAAU,SAAS;AAAA,MAC9D,QAAQ;AAAA,QACP,MAAM,QAAQ,IAAI,KAAK,OAAO,KAAK,CAAC,MAAM,WACrC,KAAqC,KAAK,KAAK,WAChD;AAAA,QACJ;AAAA,MACD;AAAA,IACD,EAAE;AAAA,IACF,QAAQ;AAAA,EACT;AACD;AAEA,MAAM,SAAqD;AAAA,EAC1D,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,MAAM;AACP;AAGO,SAAS,eACf,QAC6B;AAC7B,QAAM,EAAE,SAAS,WAAW,SAAS,OAAO,OAAO,IAAI;AACvD,QAAM,EAAE,WAAW,WAAW,IAAI;AAAA,IACjC;AAAA,IACA;AAAA,IACA,OAAO;AAAA,EACR;AACA,QAAM,cAAc,QAAQ,UAAU,KAAK;AAC3C,QAAM,EAAE,WAAW,QAAQ,OAAO,IAAI;AAAA,IACrC;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACA,QAAM,YAAY,QAAQ,QAAQ,QAAQ;AAAA,IACzC,UAAU,UAAU,WAAW;AAAA,IAC/B,OAAO,QAAQ;AAAA,IACf;AAAA,IACA,WAAW,QAAQ,eAAe,YAAY,cAAc;AAAA,IAC5D;AAAA,IACA,MAAM;AAAA,IACN,WAAW,OAAO;AAAA,EACnB,CAAC;AACD,QAAM,OAAO,UACZ,QAAQ,eAAe,aAAa,aAAa,MAAM,IACpD,IACA,UAAU,SAAS,CACvB;AAEA,MAAI,UAAU;AAEd,MAAI,OAAsB,UAAU,CAAC;AACrC,MAAI,UAAsB,MAAM;AAAA,EAAC;AACjC,QAAM,WAAW,IAAI,QAAc,CAAC,SAAS;AAC5C,cAAU;AAAA,EACX,CAAC;AAED,WAAS,UAAU;AAClB,QAAI,QAAS,QAAO,EAAE,OAAO,MAAM,UAAU,EAAE;AAC/C,UAAM,WAAW,OAAO,UAAU,WAAW,KAAK,KAAK,MAAO;AAC9D,QAAI,WAAW;AACd,aAAO,EAAE,OAAO,UAAU,CAAC,GAAoB,UAAU,EAAE;AAC5D,UAAM,QAAQ,UAAU;AAAA,MACvB,cAAc,SAAS,UAAU,UAAU,OAAO;AAAA,IACnD;AACA,QAAI,CAAC,WAAY,QAAO;AAExB,UAAM,UACL,OAAO,cAAc,YAClB,iBAAiB,OAAO,EAAE,iBAAiB,QAAQ,OAAO,QAAQ,CAAC,IACnE;AACJ,WAAO,EAAE,OAAO,WAAW,MAAM,UAAU,EAAE;AAAA,EAC9C;AAEA,WAAS,OAAO,OAAkC;AACjD,QAAI,QAAS;AACb,cAAU;AACV,QAAI,UAAU,QAAW;AACxB,aAAO;AACP,aAAO,KAAK;AAAA,IACb;AACA,cAAU,OAAO;AACjB,YAAQ;AAAA,EACT;AAEA,YAAU,SAAS;AAAA,IAClB,MAAM;AACL,UAAI,QAAS;AACb,aAAO,IAAI;AACX,aAAO,aAAa;AAAA,IACrB;AAAA,IACA,MAAM,OAAO,MAAS;AAAA,EACvB;AAEA,SAAO;AAAA,IACN,GAAG,SAAS,QAAQ;AAAA,IACpB,IAAI,OAAO;AACV,cAAQ,OAAO,UAAU,WAAW,KAAK,KAAK;AAAA,IAC/C;AAAA,IACA,IAAI,KAAK,SAAS;AACjB,gBAAU,cAAc,UAAU;AAAA,IACnC;AAAA,IACA,IAAI,QAAQ;AACX,aAAO,UAAU;AAAA,IAClB;AAAA,IACA,IAAI,MAAM,MAAM;AACf,gBAAU,eAAe;AAAA,IAC1B;AAAA,IACA,UAAU,QAAQ,UAAU,WAAW;AAAA,IACvC,IAAI,QAAQ;AACX,aAAO,UAAU,aAAa,OAAO,UAAU,SAAS;AAAA,IACzD;AAAA,IACA,MAAM,MAAM,UAAU,KAAK;AAAA,IAC3B,OAAO,MAAM,UAAU,MAAM;AAAA,IAC7B,MAAM,MAAM,OAAO,QAAQ,EAAE,KAAK;AAAA,IAClC,QAAQ,MAAM,OAAO,UAAU,CAAC,CAAC;AAAA;AAAA,IAEjC,UAAU,MACT,eAAe,OAAO,oBACnB,OAAO,IAAI,IACX,UAAU,OAAO;AAAA,IACrB,eAAe,UAAU;AACxB,gBAAU,WAAW;AACrB,aAAO;AAAA,IACR;AAAA,IACA;AAAA,EACD;AACD;",
4
+ "sourcesContent": ["import { thenable } from \"#/animate/controls.ts\";\nimport type { JsAnimation } from \"#/animate/js-animation.ts\";\nimport { iterationTime, planGenerator } from \"#/animate/plan.ts\";\nimport { cssName } from \"#/animate/style.ts\";\nimport type {\n\tAnimationOptions,\n\tAnimationState,\n\tKeyframeValue,\n} from \"#/animate/types.ts\";\nimport { linearEasing, supportsLinearEasing } from \"#/easing/linear-css.ts\";\nimport { easingToCss, resolveEasing } from \"#/easing/named.ts\";\nimport type { EasingDefinition } from \"#/easing/types.ts\";\nimport type { Generator } from \"#/generators/types.ts\";\n\nexport interface WaapiConfig {\n\treadonly element: Element;\n\t/** The CSS property the browser animates. */\n\treadonly property: string;\n\treadonly keyframes: readonly KeyframeValue[];\n\t/** A keyframe as the property's CSS value. */\n\tformat(value: KeyframeValue): string;\n\treadonly options: AnimationOptions;\n\treadonly delay: number;\n\treadonly velocity: number;\n\treadonly composite: CompositeOperation;\n\t/** Writes the value that stays once the animation is gone. */\n\tcommit(value: KeyframeValue): void;\n\tonComplete?(): void;\n}\n\nconst SAMPLE_STEP = 1 / 60;\n\nfunction cssEasing(definition: EasingDefinition, duration: number): string {\n\treturn (\n\t\teasingToCss(definition) ?? linearEasing(resolveEasing(definition), duration)\n\t);\n}\n\n/**\n * The browser keyframes and easing. Two keyframes on a spring become one\n * `linear()` easing sampled from the spring, so the compositor plays the\n * exact curve. Without `linear()`, or when the spring starts and ends at the\n * same value (a flick of velocity), the spring is sampled into keyframes.\n */\nfunction browserKeyframes(\n\tconfig: WaapiConfig,\n\tgenerator: Generator,\n\tpositional: boolean,\n): { keyframes: Keyframe[]; easing: string } {\n\tconst { keyframes, format, property, options } = config;\n\tconst duration = generator.duration;\n\tconst [from, to] = keyframes;\n\tconst springs =\n\t\toptions.type !== \"tween\" && keyframes.length === 2 && !options.ease;\n\tconst span =\n\t\ttypeof from === \"number\" && typeof to === \"number\" ? to - from : 1;\n\tif (springs && span !== 0 && supportsLinearEasing()) {\n\t\tconst progress = (t: number) => {\n\t\t\tconst { value } = generator.at(t * duration);\n\t\t\treturn positional ? value : (value - (from as number)) / span;\n\t\t};\n\t\treturn {\n\t\t\tkeyframes: keyframes.map((frame) => ({ [property]: format(frame) })),\n\t\t\teasing: linearEasing(progress, duration),\n\t\t};\n\t}\n\tif (springs && !positional) {\n\t\tconst count = Math.max(2, Math.ceil(duration / SAMPLE_STEP) + 1);\n\t\treturn {\n\t\t\tkeyframes: Array.from({ length: count }, (_unused, index) => {\n\t\t\t\tconst offset = index / (count - 1);\n\t\t\t\treturn {\n\t\t\t\t\t[property]: format(generator.at(offset * duration).value),\n\t\t\t\t\toffset,\n\t\t\t\t};\n\t\t\t}),\n\t\t\teasing: \"linear\",\n\t\t};\n\t}\n\tconst ease = options.ease ?? \"easeOut\";\n\treturn {\n\t\tkeyframes: keyframes.map((frame, index) => ({\n\t\t\t[property]: format(frame),\n\t\t\toffset: options.times?.[index] ?? index / (keyframes.length - 1),\n\t\t\teasing: cssEasing(\n\t\t\t\tArray.isArray(ease) && typeof ease[0] !== \"number\"\n\t\t\t\t\t? ((ease as readonly EasingDefinition[])[index] ?? \"linear\")\n\t\t\t\t\t: (ease as EasingDefinition),\n\t\t\t\tduration,\n\t\t\t),\n\t\t})),\n\t\teasing: \"linear\",\n\t};\n}\n\nconst STATES: Record<AnimationPlayState, AnimationState> = {\n\trunning: \"running\",\n\tpaused: \"paused\",\n\tfinished: \"finished\",\n\tidle: \"finished\",\n};\n\n/** One CSS property of one element, played by the Web Animations API. */\nexport function waapiAnimation(\n\tconfig: WaapiConfig,\n): JsAnimation<KeyframeValue> {\n\tconst { element, keyframes, options, delay, commit } = config;\n\tconst { generator, positional } = planGenerator(\n\t\tkeyframes,\n\t\toptions,\n\t\tconfig.velocity,\n\t);\n\tconst iterations = (options.repeat ?? 0) + 1;\n\tconst { keyframes: frames, easing } = browserKeyframes(\n\t\tconfig,\n\t\tgenerator,\n\t\tpositional,\n\t);\n\tconst animation = element.animate(frames, {\n\t\tduration: generator.duration * 1000,\n\t\tdelay: delay * 1000,\n\t\titerations,\n\t\tdirection: options.repeatType === \"reverse\" ? \"alternate\" : \"normal\",\n\t\teasing,\n\t\tfill: \"both\",\n\t\tcomposite: config.composite,\n\t});\n\tconst last = keyframes[\n\t\toptions.repeatType === \"reverse\" && iterations % 2 === 0\n\t\t\t? 0\n\t\t\t: keyframes.length - 1\n\t] as KeyframeValue;\n\n\t/** Played backwards, it ends on its first keyframe. */\n\tfunction end(): KeyframeValue {\n\t\treturn animation.playbackRate < 0 ? (keyframes[0] as KeyframeValue) : last;\n\t}\n\n\tlet settled = false;\n\t/** What stayed on screen when it settled: where a following animation starts. */\n\tlet held: KeyframeValue = keyframes[0] as KeyframeValue;\n\tlet resolve: () => void = () => {};\n\tconst finished = new Promise<void>((done) => {\n\t\tresolve = done;\n\t});\n\n\tfunction current() {\n\t\tif (settled) return { value: held, velocity: 0 };\n\t\tconst elapsed = (Number(animation.currentTime) || 0) / 1000 - delay;\n\t\tif (elapsed <= 0)\n\t\t\treturn { value: keyframes[0] as KeyframeValue, velocity: 0 };\n\t\tconst state = generator.at(\n\t\t\titerationTime(elapsed, generator.duration, options),\n\t\t);\n\t\tif (!positional) return state;\n\t\t// The browser mixes colours and strings itself: ask it what it painted.\n\t\tconst painted =\n\t\t\tconfig.composite === \"replace\"\n\t\t\t\t? getComputedStyle(element).getPropertyValue(cssName(config.property))\n\t\t\t\t: \"\";\n\t\treturn { value: painted || last, velocity: 0 };\n\t}\n\n\tfunction settle(value: KeyframeValue | undefined) {\n\t\tif (settled) return;\n\t\tsettled = true;\n\t\tif (value !== undefined) {\n\t\t\theld = value;\n\t\t\tcommit(value);\n\t\t}\n\t\tanimation.cancel();\n\t\tresolve();\n\t}\n\n\tanimation.finished.then(\n\t\t() => {\n\t\t\tif (settled) return;\n\t\t\tsettle(end());\n\t\t\tconfig.onComplete?.();\n\t\t},\n\t\t() => settle(undefined),\n\t);\n\n\treturn {\n\t\t...thenable(finished),\n\t\tget time() {\n\t\t\treturn (Number(animation.currentTime) || 0) / 1000;\n\t\t},\n\t\tset time(seconds) {\n\t\t\tanimation.currentTime = seconds * 1000;\n\t\t},\n\t\tget speed() {\n\t\t\treturn animation.playbackRate;\n\t\t},\n\t\tset speed(rate) {\n\t\t\tanimation.playbackRate = rate;\n\t\t},\n\t\tduration: delay + generator.duration * iterations,\n\t\tget state() {\n\t\t\treturn settled ? \"finished\" : STATES[animation.playState];\n\t\t},\n\t\tplay: () => animation.play(),\n\t\tpause: () => animation.pause(),\n\t\tstop: () => settle(current().value),\n\t\tcancel: () => settle(keyframes[0]),\n\t\t// The browser refuses to finish an endless animation: settle on its last frame instead.\n\t\tcomplete: () =>\n\t\t\titerations === Number.POSITIVE_INFINITY\n\t\t\t\t? settle(end())\n\t\t\t\t: animation.finish(),\n\t\tattachTimeline(timeline) {\n\t\t\tanimation.timeline = timeline;\n\t\t\treturn true;\n\t\t},\n\t\tcurrent,\n\t};\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"spring.d.ts","sourceRoot":"","sources":["../../src/generators/spring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,SAAS,EAET,aAAa,EACb,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AAMD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE,aAAkB,GAAG,aAAa,CAgBxE;AAoDD,wBAAgB,MAAM,CACrB,OAAO,EAAE,aAAa,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GACtE,SAAS,CA0BX"}
1
+ {"version":3,"file":"spring.d.ts","sourceRoot":"","sources":["../../src/generators/spring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,SAAS,EAET,aAAa,EACb,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AAKD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE,aAAkB,GAAG,aAAa,CAgBxE;AAyHD,wBAAgB,MAAM,CACrB,OAAO,EAAE,aAAa,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GACtE,SAAS,CAwBX"}
@@ -1,5 +1,4 @@
1
1
  const MAX_DURATION = 20;
2
- const SETTLE_STEP = 5e-3;
3
2
  function springPhysics(options = {}) {
4
3
  const { stiffness, damping, mass } = options;
5
4
  if (stiffness !== void 0 || damping !== void 0 || mass !== void 0) {
@@ -17,40 +16,90 @@ function springPhysics(options = {}) {
17
16
  mass: 1
18
17
  };
19
18
  }
20
- function oscillator(physics, x0, v0) {
19
+ function settleTime(p, q, a, threshold) {
20
+ if (q < 0) {
21
+ p = -p;
22
+ q = -q;
23
+ }
24
+ const peak = 1 / a - p / q;
25
+ function excess(t2) {
26
+ return Math.log(p + q * t2) - a * t2 - Math.log(threshold);
27
+ }
28
+ if (q === 0 || excess(Math.max(0, peak)) <= 0) {
29
+ return Math.max(0, Math.log(Math.abs(p) / threshold) / a);
30
+ }
31
+ let t = Math.max(0, peak) + 1 / a;
32
+ for (let step = 0; step < 32; step += 1) {
33
+ const next = t - excess(t) / (q / (p + q * t) - a);
34
+ if (Math.abs(next - t) < 1e-6) return next;
35
+ t = next;
36
+ }
37
+ return t;
38
+ }
39
+ function oscillator(physics, x0, v0, restDelta, restSpeed) {
21
40
  const omega = Math.sqrt(physics.stiffness / physics.mass);
22
41
  const zeta = physics.damping / (2 * Math.sqrt(physics.stiffness * physics.mass));
23
- if (Math.abs(zeta - 1) < 1e-6) {
24
- const c = v0 + omega * x0;
25
- return (t) => {
26
- const decay = Math.exp(-omega * t);
27
- return [decay * (x0 + c * t), decay * (c - omega * (x0 + c * t))];
28
- };
42
+ const critical = Math.abs(zeta - 1) < 1e-6;
43
+ const slow = omega * (zeta < 1 ? zeta : zeta - Math.sqrt(zeta * zeta - 1));
44
+ const b = v0 + zeta * omega * x0;
45
+ function near(p, q, threshold) {
46
+ return critical ? settleTime(p, q, slow, threshold) : settleTime(Math.abs(p), Math.abs(q), slow, threshold);
47
+ }
48
+ const settle = Math.max(
49
+ near(x0, b, restDelta),
50
+ near(v0, -(zeta * omega * v0 + omega * omega * x0), restSpeed)
51
+ );
52
+ if (critical) {
53
+ return [
54
+ (t) => {
55
+ const decay = Math.exp(-omega * t);
56
+ return [decay * (x0 + b * t), decay * (b - omega * (x0 + b * t))];
57
+ },
58
+ settle
59
+ ];
29
60
  }
30
61
  if (zeta < 1) {
31
62
  const omegaD = omega * Math.sqrt(1 - zeta * zeta);
32
- const b = (v0 + zeta * omega * x0) / omegaD;
33
- return (t) => {
34
- const decay = Math.exp(-zeta * omega * t);
35
- const cos = Math.cos(omegaD * t);
36
- const sin = Math.sin(omegaD * t);
37
- const x = x0 * cos + b * sin;
38
- return [
39
- decay * x,
40
- decay * (-zeta * omega * x + omegaD * (b * cos - x0 * sin))
41
- ];
42
- };
63
+ const sine = b / omegaD;
64
+ const amplitude = Math.hypot(x0, sine);
65
+ return [
66
+ (t) => {
67
+ const decay = Math.exp(-slow * t);
68
+ const cos = Math.cos(omegaD * t);
69
+ const sin = Math.sin(omegaD * t);
70
+ const x = x0 * cos + sine * sin;
71
+ return [decay * x, decay * (-slow * x + b * cos - omegaD * x0 * sin)];
72
+ },
73
+ Math.min(
74
+ settle,
75
+ Math.max(
76
+ settleTime(amplitude, 0, slow, restDelta),
77
+ settleTime(amplitude * omega, 0, slow, restSpeed)
78
+ )
79
+ )
80
+ ];
43
81
  }
44
- const root = omega * Math.sqrt(zeta * zeta - 1);
45
- const r1 = -zeta * omega + root;
46
- const r2 = -zeta * omega - root;
82
+ const r1 = -slow;
83
+ const r2 = -2 * zeta * omega - r1;
47
84
  const c2 = (v0 - r1 * x0) / (r2 - r1);
48
85
  const c1 = x0 - c2;
49
- return (t) => {
50
- const e1 = c1 * Math.exp(r1 * t);
51
- const e2 = c2 * Math.exp(r2 * t);
52
- return [e1 + e2, r1 * e1 + r2 * e2];
53
- };
86
+ return [
87
+ (t) => {
88
+ const e1 = c1 * Math.exp(r1 * t);
89
+ const e2 = c2 * Math.exp(r2 * t);
90
+ return [e1 + e2, r1 * e1 + r2 * e2];
91
+ },
92
+ // Each mode on its own, with half of each threshold.
93
+ Math.min(
94
+ settle,
95
+ Math.max(
96
+ settleTime(c1, 0, -r1, restDelta / 2),
97
+ settleTime(c2, 0, -r2, restDelta / 2),
98
+ settleTime(r1 * c1, 0, -r1, restSpeed / 2),
99
+ settleTime(r2 * c2, 0, -r2, restSpeed / 2)
100
+ )
101
+ )
102
+ ];
54
103
  }
55
104
  function restThresholds(options, span) {
56
105
  const restDelta = options.restDelta ?? Math.max(span, 1) * 1e-3;
@@ -59,16 +108,15 @@ function restThresholds(options, span) {
59
108
  function spring(options) {
60
109
  const { from, to } = options;
61
110
  const velocity = options.velocity ?? 0;
62
- const displacement = oscillator(springPhysics(options), from - to, velocity);
63
111
  const { restDelta, restSpeed } = restThresholds(options, Math.abs(to - from));
64
- function atRest(t) {
65
- const [x, v] = displacement(t);
66
- return Math.abs(x) <= restDelta && Math.abs(v) <= restSpeed;
67
- }
68
- let duration = 0;
69
- while (duration < MAX_DURATION && !atRest(duration)) {
70
- duration += SETTLE_STEP;
71
- }
112
+ const [displacement, settle] = oscillator(
113
+ springPhysics(options),
114
+ from - to,
115
+ velocity,
116
+ restDelta,
117
+ restSpeed
118
+ );
119
+ const duration = settle < MAX_DURATION ? settle : MAX_DURATION;
72
120
  return {
73
121
  duration,
74
122
  at(elapsed) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/generators/spring.ts"],
4
- "sourcesContent": ["import type {\n\tGenerator,\n\tGeneratorState,\n\tSpringOptions,\n} from \"#/generators/types.ts\";\n\nexport interface SpringPhysics {\n\treadonly stiffness: number;\n\treadonly damping: number;\n\treadonly mass: number;\n}\n\n/** A spring that never settles would animate forever: stop looking after this. */\nconst MAX_DURATION = 20;\nconst SETTLE_STEP = 0.005;\n\n/**\n * Duration and bounce to physics, SwiftUI's mapping: the undamped period is\n * the perceived duration, and the damping ratio is `1 - bounce`.\n */\nexport function springPhysics(options: SpringOptions = {}): SpringPhysics {\n\tconst { stiffness, damping, mass } = options;\n\tif (stiffness !== undefined || damping !== undefined || mass !== undefined) {\n\t\treturn {\n\t\t\tstiffness: stiffness ?? 100,\n\t\t\tdamping: damping ?? 10,\n\t\t\tmass: mass ?? 1,\n\t\t};\n\t}\n\tconst duration = Math.max(options.duration ?? 0.3, 0.001);\n\tconst bounce = Math.min(Math.max(options.bounce ?? 0, 0), 0.95);\n\treturn {\n\t\tstiffness: ((2 * Math.PI) / duration) ** 2,\n\t\tdamping: (4 * Math.PI * (1 - bounce)) / duration,\n\t\tmass: 1,\n\t};\n}\n\ntype Displacement = (t: number) => readonly [number, number];\n\n/** The damped oscillator's closed form: displacement from the target and its velocity at `t`. */\nfunction oscillator(\n\tphysics: SpringPhysics,\n\tx0: number,\n\tv0: number,\n): Displacement {\n\tconst omega = Math.sqrt(physics.stiffness / physics.mass);\n\tconst zeta =\n\t\tphysics.damping / (2 * Math.sqrt(physics.stiffness * physics.mass));\n\tif (Math.abs(zeta - 1) < 1e-6) {\n\t\tconst c = v0 + omega * x0;\n\t\treturn (t) => {\n\t\t\tconst decay = Math.exp(-omega * t);\n\t\t\treturn [decay * (x0 + c * t), decay * (c - omega * (x0 + c * t))];\n\t\t};\n\t}\n\tif (zeta < 1) {\n\t\tconst omegaD = omega * Math.sqrt(1 - zeta * zeta);\n\t\tconst b = (v0 + zeta * omega * x0) / omegaD;\n\t\treturn (t) => {\n\t\t\tconst decay = Math.exp(-zeta * omega * t);\n\t\t\tconst cos = Math.cos(omegaD * t);\n\t\t\tconst sin = Math.sin(omegaD * t);\n\t\t\tconst x = x0 * cos + b * sin;\n\t\t\treturn [\n\t\t\t\tdecay * x,\n\t\t\t\tdecay * (-zeta * omega * x + omegaD * (b * cos - x0 * sin)),\n\t\t\t];\n\t\t};\n\t}\n\tconst root = omega * Math.sqrt(zeta * zeta - 1);\n\tconst r1 = -zeta * omega + root;\n\tconst r2 = -zeta * omega - root;\n\tconst c2 = (v0 - r1 * x0) / (r2 - r1);\n\tconst c1 = x0 - c2;\n\treturn (t) => {\n\t\tconst e1 = c1 * Math.exp(r1 * t);\n\t\tconst e2 = c2 * Math.exp(r2 * t);\n\t\treturn [e1 + e2, r1 * e1 + r2 * e2];\n\t};\n}\n\n/** Rest thresholds scale with the motion, so 0\u21921 and 0\u21921000 both settle to the eye. */\nfunction restThresholds(options: SpringOptions, span: number) {\n\tconst restDelta = options.restDelta ?? Math.max(span, 1) * 0.001;\n\treturn { restDelta, restSpeed: options.restSpeed ?? restDelta * 10 };\n}\n\nexport function spring(\n\toptions: SpringOptions & { from: number; to: number; velocity?: number },\n): Generator {\n\tconst { from, to } = options;\n\tconst velocity = options.velocity ?? 0;\n\tconst displacement = oscillator(springPhysics(options), from - to, velocity);\n\tconst { restDelta, restSpeed } = restThresholds(options, Math.abs(to - from));\n\n\tfunction atRest(t: number): boolean {\n\t\tconst [x, v] = displacement(t);\n\t\treturn Math.abs(x) <= restDelta && Math.abs(v) <= restSpeed;\n\t}\n\n\tlet duration = 0;\n\twhile (duration < MAX_DURATION && !atRest(duration)) {\n\t\tduration += SETTLE_STEP;\n\t}\n\n\treturn {\n\t\tduration,\n\t\tat(elapsed): GeneratorState {\n\t\t\tif (elapsed >= duration) {\n\t\t\t\treturn { value: to, velocity: 0, done: true };\n\t\t\t}\n\t\t\tconst [x, v] = displacement(Math.max(0, elapsed));\n\t\t\treturn { value: to + x, velocity: v, done: false };\n\t\t},\n\t};\n}\n"],
5
- "mappings": "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",
6
- "names": []
4
+ "sourcesContent": ["import type {\n\tGenerator,\n\tGeneratorState,\n\tSpringOptions,\n} from \"#/generators/types.ts\";\n\nexport interface SpringPhysics {\n\treadonly stiffness: number;\n\treadonly damping: number;\n\treadonly mass: number;\n}\n\n/** A spring that never settles would animate forever: stop it after this. */\nconst MAX_DURATION = 20;\n\n/**\n * Duration and bounce to physics, SwiftUI's mapping: the undamped period is\n * the perceived duration, and the damping ratio is `1 - bounce`.\n */\nexport function springPhysics(options: SpringOptions = {}): SpringPhysics {\n\tconst { stiffness, damping, mass } = options;\n\tif (stiffness !== undefined || damping !== undefined || mass !== undefined) {\n\t\treturn {\n\t\t\tstiffness: stiffness ?? 100,\n\t\t\tdamping: damping ?? 10,\n\t\t\tmass: mass ?? 1,\n\t\t};\n\t}\n\tconst duration = Math.max(options.duration ?? 0.3, 0.001);\n\tconst bounce = Math.min(Math.max(options.bounce ?? 0, 0), 0.95);\n\treturn {\n\t\tstiffness: ((2 * Math.PI) / duration) ** 2,\n\t\tdamping: (4 * Math.PI * (1 - bounce)) / duration,\n\t\tmass: 1,\n\t};\n}\n\ntype Displacement = (t: number) => readonly [number, number];\n\n/**\n * When `|p + q\u00B7t|\u00B7e^(\u2212a\u00B7t)`, a bound on a distance or a speed, drops under\n * `threshold` for good. Newton on its logarithm, which is concave past the\n * peak: every step lands at or past the root, so it errs late, never early.\n */\nfunction settleTime(p: number, q: number, a: number, threshold: number) {\n\tif (q < 0) {\n\t\tp = -p;\n\t\tq = -q;\n\t}\n\tconst peak = 1 / a - p / q;\n\tfunction excess(t: number): number {\n\t\treturn Math.log(p + q * t) - a * t - Math.log(threshold);\n\t}\n\t// No hump above the threshold: only the stretch before it crosses zero, if it does.\n\tif (q === 0 || excess(Math.max(0, peak)) <= 0) {\n\t\treturn Math.max(0, Math.log(Math.abs(p) / threshold) / a);\n\t}\n\tlet t = Math.max(0, peak) + 1 / a;\n\tfor (let step = 0; step < 32; step += 1) {\n\t\tconst next = t - excess(t) / (q / (p + q * t) - a);\n\t\tif (Math.abs(next - t) < 1e-6) return next;\n\t\tt = next;\n\t}\n\treturn t;\n}\n\n/**\n * The damped oscillator's closed form: displacement from the target and its\n * velocity at `t`, and when both stay under the rest thresholds.\n */\nfunction oscillator(\n\tphysics: SpringPhysics,\n\tx0: number,\n\tv0: number,\n\trestDelta: number,\n\trestSpeed: number,\n): readonly [Displacement, number] {\n\tconst omega = Math.sqrt(physics.stiffness / physics.mass);\n\tconst zeta =\n\t\tphysics.damping / (2 * Math.sqrt(physics.stiffness * physics.mass));\n\tconst critical = Math.abs(zeta - 1) < 1e-6;\n\tconst slow = omega * (zeta < 1 ? zeta : zeta - Math.sqrt(zeta * zeta - 1));\n\tconst b = v0 + zeta * omega * x0;\n\t/**\n\t * Exact at critical damping. Elsewhere a bound, from sin(\u03C9t) \u2264 \u03C9t and\n\t * sinh(\u03B3t)/\u03B3 \u2264 t\u00B7e^(\u03B3t), that stays tight near critical damping, where\n\t * the amplitudes blow up while the motion doesn't.\n\t */\n\tfunction near(p: number, q: number, threshold: number): number {\n\t\treturn critical\n\t\t\t? settleTime(p, q, slow, threshold)\n\t\t\t: settleTime(Math.abs(p), Math.abs(q), slow, threshold);\n\t}\n\tconst settle = Math.max(\n\t\tnear(x0, b, restDelta),\n\t\tnear(v0, -(zeta * omega * v0 + omega * omega * x0), restSpeed),\n\t);\n\tif (critical) {\n\t\treturn [\n\t\t\t(t) => {\n\t\t\t\tconst decay = Math.exp(-omega * t);\n\t\t\t\treturn [decay * (x0 + b * t), decay * (b - omega * (x0 + b * t))];\n\t\t\t},\n\t\t\tsettle,\n\t\t];\n\t}\n\tif (zeta < 1) {\n\t\tconst omegaD = omega * Math.sqrt(1 - zeta * zeta);\n\t\tconst sine = b / omegaD;\n\t\tconst amplitude = Math.hypot(x0, sine);\n\t\treturn [\n\t\t\t(t) => {\n\t\t\t\tconst decay = Math.exp(-slow * t);\n\t\t\t\tconst cos = Math.cos(omegaD * t);\n\t\t\t\tconst sin = Math.sin(omegaD * t);\n\t\t\t\tconst x = x0 * cos + sine * sin;\n\t\t\t\treturn [decay * x, decay * (-slow * x + b * cos - omegaD * x0 * sin)];\n\t\t\t},\n\t\t\tMath.min(\n\t\t\t\tsettle,\n\t\t\t\tMath.max(\n\t\t\t\t\tsettleTime(amplitude, 0, slow, restDelta),\n\t\t\t\t\tsettleTime(amplitude * omega, 0, slow, restSpeed),\n\t\t\t\t),\n\t\t\t),\n\t\t];\n\t}\n\tconst r1 = -slow;\n\tconst r2 = -2 * zeta * omega - r1;\n\tconst c2 = (v0 - r1 * x0) / (r2 - r1);\n\tconst c1 = x0 - c2;\n\treturn [\n\t\t(t) => {\n\t\t\tconst e1 = c1 * Math.exp(r1 * t);\n\t\t\tconst e2 = c2 * Math.exp(r2 * t);\n\t\t\treturn [e1 + e2, r1 * e1 + r2 * e2];\n\t\t},\n\t\t// Each mode on its own, with half of each threshold.\n\t\tMath.min(\n\t\t\tsettle,\n\t\t\tMath.max(\n\t\t\t\tsettleTime(c1, 0, -r1, restDelta / 2),\n\t\t\t\tsettleTime(c2, 0, -r2, restDelta / 2),\n\t\t\t\tsettleTime(r1 * c1, 0, -r1, restSpeed / 2),\n\t\t\t\tsettleTime(r2 * c2, 0, -r2, restSpeed / 2),\n\t\t\t),\n\t\t),\n\t];\n}\n\n/** Rest thresholds scale with the motion, so 0\u21921 and 0\u21921000 both settle to the eye. */\nfunction restThresholds(options: SpringOptions, span: number) {\n\tconst restDelta = options.restDelta ?? Math.max(span, 1) * 0.001;\n\treturn { restDelta, restSpeed: options.restSpeed ?? restDelta * 10 };\n}\n\nexport function spring(\n\toptions: SpringOptions & { from: number; to: number; velocity?: number },\n): Generator {\n\tconst { from, to } = options;\n\tconst velocity = options.velocity ?? 0;\n\tconst { restDelta, restSpeed } = restThresholds(options, Math.abs(to - from));\n\tconst [displacement, settle] = oscillator(\n\t\tspringPhysics(options),\n\t\tfrom - to,\n\t\tvelocity,\n\t\trestDelta,\n\t\trestSpeed,\n\t);\n\t// An undamped spring never settles, and degenerate physics yields NaN.\n\tconst duration = settle < MAX_DURATION ? settle : MAX_DURATION;\n\n\treturn {\n\t\tduration,\n\t\tat(elapsed): GeneratorState {\n\t\t\tif (elapsed >= duration) {\n\t\t\t\treturn { value: to, velocity: 0, done: true };\n\t\t\t}\n\t\t\tconst [x, v] = displacement(Math.max(0, elapsed));\n\t\t\treturn { value: to + x, velocity: v, done: false };\n\t\t},\n\t};\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["t"]
7
7
  }
@@ -8,7 +8,7 @@ export interface Rgba {
8
8
  type Channels = readonly [number, number, number, number];
9
9
  /** Oklab is mixed in, and also a syntax a page can write colours in. */
10
10
  export declare function oklabToRgba([l, a, b, alpha]: Channels): Rgba;
11
- /** A colour written out in full; `undefined` for what only the page can resolve (`var()`, names). */
11
+ /** A colour written out in full, or a name the browser knows; `undefined` for what only the page can resolve (`var()`, `currentColor`). */
12
12
  export declare function parseColor(text: string): Rgba | undefined;
13
13
  export {};
14
14
  //# sourceMappingURL=color-parse.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"color-parse.d.ts","sourceRoot":"","sources":["../../src/interpolate/color-parse.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,MAAM,WAAW,IAAI;IACpB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AAED,KAAK,QAAQ,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAoC1D,wEAAwE;AACxE,wBAAgB,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,QAAQ,GAAG,IAAI,CAa5D;AA6DD,qGAAqG;AACrG,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAezD"}
1
+ {"version":3,"file":"color-parse.d.ts","sourceRoot":"","sources":["../../src/interpolate/color-parse.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,MAAM,WAAW,IAAI;IACpB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AAED,KAAK,QAAQ,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAoC1D,wEAAwE;AACxE,wBAAgB,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,QAAQ,GAAG,IAAI,CAa5D;AA0FD,2IAA2I;AAC3I,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAezD"}
@@ -88,6 +88,24 @@ function parseHex(hex) {
88
88
  alpha: digits.length === 8 ? byte(6) : 1
89
89
  };
90
90
  }
91
+ const names = /* @__PURE__ */ new Map();
92
+ let probe;
93
+ const SENTINEL = "#010203";
94
+ function parseName(name) {
95
+ if (name === "currentcolor" || !/^[a-z]+$/.test(name)) return void 0;
96
+ if (!names.has(name)) {
97
+ probe ??= typeof document === "undefined" ? null : document.createElement("canvas").getContext("2d");
98
+ let resolved;
99
+ if (probe) {
100
+ probe.fillStyle = SENTINEL;
101
+ probe.fillStyle = name;
102
+ const spelled = String(probe.fillStyle);
103
+ resolved = spelled === SENTINEL ? void 0 : parseColor(spelled);
104
+ }
105
+ names.set(name, resolved);
106
+ }
107
+ return names.get(name);
108
+ }
91
109
  function parseColor(text) {
92
110
  const trimmed = text.trim();
93
111
  if (trimmed.toLowerCase() === "transparent") {
@@ -98,7 +116,7 @@ function parseColor(text) {
98
116
  }
99
117
  const match = FUNCTION.exec(trimmed);
100
118
  if (match === null) {
101
- return void 0;
119
+ return parseName(trimmed.toLowerCase());
102
120
  }
103
121
  const name = match[1].toLowerCase().replace(/a$/, "");
104
122
  const args = match[2].split(/[\s,/]+/).filter(Boolean);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/interpolate/color-parse.ts"],
4
- "sourcesContent": ["/** A colour in sRGB, every channel 0\u20131. */\nexport interface Rgba {\n\treadonly r: number;\n\treadonly g: number;\n\treadonly b: number;\n\treadonly alpha: number;\n}\n\ntype Channels = readonly [number, number, number, number];\ntype Parser = (args: readonly string[]) => Rgba | undefined;\n\nconst FUNCTION = /^(rgba?|hsla?|oklch|oklab)\\(\\s*([^()]*)\\)$/i;\nconst HUE_UNITS: Readonly<Record<string, number>> = {\n\tdeg: 1,\n\tgrad: 0.9,\n\trad: 180 / Math.PI,\n\tturn: 360,\n};\n\n/** A number, a percentage of `percentScale`, or `none`. */\nfunction channel(text: string | undefined, percentScale: number): number {\n\tif (text === undefined || text === \"none\") {\n\t\treturn 0;\n\t}\n\treturn text.endsWith(\"%\")\n\t\t? (Number.parseFloat(text) / 100) * percentScale\n\t\t: Number.parseFloat(text);\n}\n\nfunction hue(text: string | undefined): number {\n\tconst unit = text?.match(/[a-z]+$/i)?.[0].toLowerCase() ?? \"deg\";\n\treturn channel(text, 1) * (HUE_UNITS[unit] ?? 1);\n}\n\nfunction alphaOf(args: readonly string[]): number {\n\treturn args[3] === undefined ? 1 : channel(args[3], 1);\n}\n\nfunction linearToSrgb(value: number): number {\n\treturn value <= 0.0031308\n\t\t? 12.92 * value\n\t\t: 1.055 * value ** (1 / 2.4) - 0.055;\n}\n\n/** Oklab is mixed in, and also a syntax a page can write colours in. */\nexport function oklabToRgba([l, a, b, alpha]: Channels): Rgba {\n\tconst lms = [\n\t\t(l + 0.3963377774 * a + 0.2158037573 * b) ** 3,\n\t\t(l - 0.1055613458 * a - 0.0638541728 * b) ** 3,\n\t\t(l - 0.0894841775 * a - 1.291485548 * b) ** 3,\n\t] as const;\n\tconst [x, y, z] = lms;\n\treturn {\n\t\tr: linearToSrgb(4.0767416621 * x - 3.3077115913 * y + 0.2309699292 * z),\n\t\tg: linearToSrgb(-1.2684380046 * x + 2.6097574011 * y - 0.3413193965 * z),\n\t\tb: linearToSrgb(-0.0041960863 * x - 0.7034186147 * y + 1.707614701 * z),\n\t\talpha,\n\t};\n}\n\nfunction hslToRgba(h: number, s: number, l: number, alpha: number): Rgba {\n\tconst amount = s * Math.min(l, 1 - l);\n\tfunction f(n: number) {\n\t\tconst k = (n + h / 30) % 12;\n\t\treturn l - amount * Math.max(-1, Math.min(k - 3, 9 - k, 1));\n\t}\n\treturn { r: f(0), g: f(8), b: f(4), alpha };\n}\n\nconst PARSERS: Readonly<Record<string, Parser>> = {\n\trgb: (args) => ({\n\t\tr: channel(args[0], 255) / 255,\n\t\tg: channel(args[1], 255) / 255,\n\t\tb: channel(args[2], 255) / 255,\n\t\talpha: alphaOf(args),\n\t}),\n\thsl: (args) =>\n\t\thslToRgba(\n\t\t\t((hue(args[0]) % 360) + 360) % 360,\n\t\t\tchannel(args[1], 100) / 100,\n\t\t\tchannel(args[2], 100) / 100,\n\t\t\talphaOf(args),\n\t\t),\n\toklab: (args) =>\n\t\toklabToRgba([\n\t\t\tchannel(args[0], 1),\n\t\t\tchannel(args[1], 0.4),\n\t\t\tchannel(args[2], 0.4),\n\t\t\talphaOf(args),\n\t\t]),\n\toklch: (args) => {\n\t\tconst chroma = channel(args[1], 0.4);\n\t\tconst angle = (hue(args[2]) * Math.PI) / 180;\n\t\treturn oklabToRgba([\n\t\t\tchannel(args[0], 1),\n\t\t\tchroma * Math.cos(angle),\n\t\t\tchroma * Math.sin(angle),\n\t\t\talphaOf(args),\n\t\t]);\n\t},\n};\n\nfunction parseHex(hex: string): Rgba | undefined {\n\tconst digits =\n\t\thex.length <= 4 ? [...hex].map((digit) => digit + digit).join(\"\") : hex;\n\tif (!/^[0-9a-f]{6}(?:[0-9a-f]{2})?$/i.test(digits)) {\n\t\treturn undefined;\n\t}\n\tfunction byte(index: number) {\n\t\treturn Number.parseInt(digits.slice(index, index + 2), 16) / 255;\n\t}\n\treturn {\n\t\tr: byte(0),\n\t\tg: byte(2),\n\t\tb: byte(4),\n\t\talpha: digits.length === 8 ? byte(6) : 1,\n\t};\n}\n\n/** A colour written out in full; `undefined` for what only the page can resolve (`var()`, names). */\nexport function parseColor(text: string): Rgba | undefined {\n\tconst trimmed = text.trim();\n\tif (trimmed.toLowerCase() === \"transparent\") {\n\t\treturn { r: 0, g: 0, b: 0, alpha: 0 };\n\t}\n\tif (trimmed.startsWith(\"#\")) {\n\t\treturn parseHex(trimmed.slice(1));\n\t}\n\tconst match = FUNCTION.exec(trimmed);\n\tif (match === null) {\n\t\treturn undefined;\n\t}\n\tconst name = (match[1] as string).toLowerCase().replace(/a$/, \"\");\n\tconst args = (match[2] as string).split(/[\\s,/]+/).filter(Boolean);\n\treturn PARSERS[name]?.(args);\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/** A colour in sRGB, every channel 0\u20131. */\nexport interface Rgba {\n\treadonly r: number;\n\treadonly g: number;\n\treadonly b: number;\n\treadonly alpha: number;\n}\n\ntype Channels = readonly [number, number, number, number];\ntype Parser = (args: readonly string[]) => Rgba | undefined;\n\nconst FUNCTION = /^(rgba?|hsla?|oklch|oklab)\\(\\s*([^()]*)\\)$/i;\nconst HUE_UNITS: Readonly<Record<string, number>> = {\n\tdeg: 1,\n\tgrad: 0.9,\n\trad: 180 / Math.PI,\n\tturn: 360,\n};\n\n/** A number, a percentage of `percentScale`, or `none`. */\nfunction channel(text: string | undefined, percentScale: number): number {\n\tif (text === undefined || text === \"none\") {\n\t\treturn 0;\n\t}\n\treturn text.endsWith(\"%\")\n\t\t? (Number.parseFloat(text) / 100) * percentScale\n\t\t: Number.parseFloat(text);\n}\n\nfunction hue(text: string | undefined): number {\n\tconst unit = text?.match(/[a-z]+$/i)?.[0].toLowerCase() ?? \"deg\";\n\treturn channel(text, 1) * (HUE_UNITS[unit] ?? 1);\n}\n\nfunction alphaOf(args: readonly string[]): number {\n\treturn args[3] === undefined ? 1 : channel(args[3], 1);\n}\n\nfunction linearToSrgb(value: number): number {\n\treturn value <= 0.0031308\n\t\t? 12.92 * value\n\t\t: 1.055 * value ** (1 / 2.4) - 0.055;\n}\n\n/** Oklab is mixed in, and also a syntax a page can write colours in. */\nexport function oklabToRgba([l, a, b, alpha]: Channels): Rgba {\n\tconst lms = [\n\t\t(l + 0.3963377774 * a + 0.2158037573 * b) ** 3,\n\t\t(l - 0.1055613458 * a - 0.0638541728 * b) ** 3,\n\t\t(l - 0.0894841775 * a - 1.291485548 * b) ** 3,\n\t] as const;\n\tconst [x, y, z] = lms;\n\treturn {\n\t\tr: linearToSrgb(4.0767416621 * x - 3.3077115913 * y + 0.2309699292 * z),\n\t\tg: linearToSrgb(-1.2684380046 * x + 2.6097574011 * y - 0.3413193965 * z),\n\t\tb: linearToSrgb(-0.0041960863 * x - 0.7034186147 * y + 1.707614701 * z),\n\t\talpha,\n\t};\n}\n\nfunction hslToRgba(h: number, s: number, l: number, alpha: number): Rgba {\n\tconst amount = s * Math.min(l, 1 - l);\n\tfunction f(n: number) {\n\t\tconst k = (n + h / 30) % 12;\n\t\treturn l - amount * Math.max(-1, Math.min(k - 3, 9 - k, 1));\n\t}\n\treturn { r: f(0), g: f(8), b: f(4), alpha };\n}\n\nconst PARSERS: Readonly<Record<string, Parser>> = {\n\trgb: (args) => ({\n\t\tr: channel(args[0], 255) / 255,\n\t\tg: channel(args[1], 255) / 255,\n\t\tb: channel(args[2], 255) / 255,\n\t\talpha: alphaOf(args),\n\t}),\n\thsl: (args) =>\n\t\thslToRgba(\n\t\t\t((hue(args[0]) % 360) + 360) % 360,\n\t\t\tchannel(args[1], 100) / 100,\n\t\t\tchannel(args[2], 100) / 100,\n\t\t\talphaOf(args),\n\t\t),\n\toklab: (args) =>\n\t\toklabToRgba([\n\t\t\tchannel(args[0], 1),\n\t\t\tchannel(args[1], 0.4),\n\t\t\tchannel(args[2], 0.4),\n\t\t\talphaOf(args),\n\t\t]),\n\toklch: (args) => {\n\t\tconst chroma = channel(args[1], 0.4);\n\t\tconst angle = (hue(args[2]) * Math.PI) / 180;\n\t\treturn oklabToRgba([\n\t\t\tchannel(args[0], 1),\n\t\t\tchroma * Math.cos(angle),\n\t\t\tchroma * Math.sin(angle),\n\t\t\talphaOf(args),\n\t\t]);\n\t},\n};\n\nfunction parseHex(hex: string): Rgba | undefined {\n\tconst digits =\n\t\thex.length <= 4 ? [...hex].map((digit) => digit + digit).join(\"\") : hex;\n\tif (!/^[0-9a-f]{6}(?:[0-9a-f]{2})?$/i.test(digits)) {\n\t\treturn undefined;\n\t}\n\tfunction byte(index: number) {\n\t\treturn Number.parseInt(digits.slice(index, index + 2), 16) / 255;\n\t}\n\treturn {\n\t\tr: byte(0),\n\t\tg: byte(2),\n\t\tb: byte(4),\n\t\talpha: digits.length === 8 ? byte(6) : 1,\n\t};\n}\n\nconst names = new Map<string, Rgba | undefined>();\nlet probe: CanvasRenderingContext2D | null | undefined;\nconst SENTINEL = \"#010203\";\n\n/**\n * A colour name (`red`, `rebeccapurple`) as the browser spells it out: a\n * canvas drops what isn't a colour (`auto`, `none`) and hands back hex for\n * what is. The 148 names would weigh more than this; without a browser a\n * name stays unknown and the page mixes it.\n */\nfunction parseName(name: string): Rgba | undefined {\n\tif (name === \"currentcolor\" || !/^[a-z]+$/.test(name)) return undefined;\n\tif (!names.has(name)) {\n\t\tprobe ??=\n\t\t\ttypeof document === \"undefined\"\n\t\t\t\t? null\n\t\t\t\t: document.createElement(\"canvas\").getContext(\"2d\");\n\t\tlet resolved: Rgba | undefined;\n\t\tif (probe) {\n\t\t\tprobe.fillStyle = SENTINEL;\n\t\t\tprobe.fillStyle = name;\n\t\t\tconst spelled = String(probe.fillStyle);\n\t\t\tresolved = spelled === SENTINEL ? undefined : parseColor(spelled);\n\t\t}\n\t\tnames.set(name, resolved);\n\t}\n\treturn names.get(name);\n}\n\n/** A colour written out in full, or a name the browser knows; `undefined` for what only the page can resolve (`var()`, `currentColor`). */\nexport function parseColor(text: string): Rgba | undefined {\n\tconst trimmed = text.trim();\n\tif (trimmed.toLowerCase() === \"transparent\") {\n\t\treturn { r: 0, g: 0, b: 0, alpha: 0 };\n\t}\n\tif (trimmed.startsWith(\"#\")) {\n\t\treturn parseHex(trimmed.slice(1));\n\t}\n\tconst match = FUNCTION.exec(trimmed);\n\tif (match === null) {\n\t\treturn parseName(trimmed.toLowerCase());\n\t}\n\tconst name = (match[1] as string).toLowerCase().replace(/a$/, \"\");\n\tconst args = (match[2] as string).split(/[\\s,/]+/).filter(Boolean);\n\treturn PARSERS[name]?.(args);\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }