@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 +4 -1
- package/dist/animate/js-animation.d.ts.map +1 -1
- package/dist/animate/js-animation.js +13 -11
- package/dist/animate/js-animation.js.map +2 -2
- package/dist/animate/style.d.ts +1 -1
- package/dist/animate/style.d.ts.map +1 -1
- package/dist/animate/style.js +18 -1
- package/dist/animate/style.js.map +2 -2
- package/dist/animate/types.d.ts +2 -2
- package/dist/animate/waapi.d.ts.map +1 -1
- package/dist/animate/waapi.js +5 -2
- package/dist/animate/waapi.js.map +2 -2
- package/dist/generators/spring.d.ts.map +1 -1
- package/dist/generators/spring.js +84 -36
- package/dist/generators/spring.js.map +3 -3
- package/dist/interpolate/color-parse.d.ts +1 -1
- package/dist/interpolate/color-parse.d.ts.map +1 -1
- package/dist/interpolate/color-parse.js +19 -1
- package/dist/interpolate/color-parse.js.map +2 -2
- package/dist/interpolate/mix.d.ts.map +1 -1
- package/dist/interpolate/mix.js.map +2 -2
- package/dist/sequence/sequence.d.ts.map +1 -1
- package/dist/sequence/sequence.js +32 -5
- package/dist/sequence/sequence.js.map +2 -2
- package/package.json +1 -1
- package/src/animate/js-animation.ts +15 -11
- package/src/animate/style.ts +28 -1
- package/src/animate/types.ts +2 -2
- package/src/animate/waapi.ts +7 -2
- package/src/generators/spring.ts +106 -40
- package/src/interpolate/color-parse.ts +31 -2
- package/src/interpolate/mix.ts +3 -2
- package/src/sequence/sequence.ts +61 -15
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,
|
|
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.
|
|
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
|
-
|
|
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
|
|
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\
|
|
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;AAEA,WAAS,KAAK,MAAiB;AAC9B,WAAO,KAAK,IAAI,GAAG,QAAS,KAAK,YAAY,YAAY,MAAQ,KAAK;
|
|
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": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/animate/style.d.ts
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/animate/style.js
CHANGED
|
@@ -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": "
|
|
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
|
}
|
package/dist/animate/types.d.ts
CHANGED
|
@@ -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
|
|
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,
|
|
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"}
|
package/dist/animate/waapi.js
CHANGED
|
@@ -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(
|
|
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(
|
|
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(
|
|
5
|
-
"mappings": "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
|
|
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": "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;AAGA,WAAS,MAAqB;AAC7B,WAAO,UAAU,eAAe,IAAK,UAAU,CAAC,IAAsB;AAAA,EACvE;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,CAAC;AACZ,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,CAAC,IACZ,UAAU,OAAO;AAAA,IACrB,eAAe,UAAU;AACxB,gBAAU,WAAW;AACrB,aAAO;AAAA,IACR;AAAA,IACA;AAAA,EACD;AACD;",
|
|
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;
|
|
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
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
decay *
|
|
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
|
|
45
|
-
const
|
|
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
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
|
5
|
-
"mappings": "AAaA,MAAM,eAAe;
|
|
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()`,
|
|
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;
|
|
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
|
|
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()`,
|
|
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
|
}
|