@voila.dev/motion 0.0.0-stage → 5.0.113
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 +105 -2
- package/dist/accessibility/reduced-motion.d.ts +13 -0
- package/dist/accessibility/reduced-motion.d.ts.map +1 -0
- package/dist/accessibility/reduced-motion.js +56 -0
- package/dist/accessibility/reduced-motion.js.map +7 -0
- package/dist/animate/animate.d.ts +21 -0
- package/dist/animate/animate.d.ts.map +1 -0
- package/dist/animate/animate.js +76 -0
- package/dist/animate/animate.js.map +7 -0
- package/dist/animate/controls.d.ts +16 -0
- package/dist/animate/controls.d.ts.map +1 -0
- package/dist/animate/controls.js +62 -0
- package/dist/animate/controls.js.map +7 -0
- package/dist/animate/dom.d.ts +11 -0
- package/dist/animate/dom.d.ts.map +1 -0
- package/dist/animate/dom.js +112 -0
- package/dist/animate/dom.js.map +7 -0
- package/dist/animate/js-animation.d.ts +25 -0
- package/dist/animate/js-animation.d.ts.map +1 -0
- package/dist/animate/js-animation.js +117 -0
- package/dist/animate/js-animation.js.map +7 -0
- package/dist/animate/plan.d.ts +17 -0
- package/dist/animate/plan.d.ts.map +1 -0
- package/dist/animate/plan.js +48 -0
- package/dist/animate/plan.js.map +7 -0
- package/dist/animate/style.d.ts +27 -0
- package/dist/animate/style.d.ts.map +1 -0
- package/dist/animate/style.js +96 -0
- package/dist/animate/style.js.map +7 -0
- package/dist/animate/types.d.ts +62 -0
- package/dist/animate/types.d.ts.map +1 -0
- package/dist/animate/types.js +1 -0
- package/dist/animate/types.js.map +7 -0
- package/dist/animate/value.d.ts +13 -0
- package/dist/animate/value.d.ts.map +1 -0
- package/dist/animate/value.js +71 -0
- package/dist/animate/value.js.map +7 -0
- package/dist/animate/waapi.d.ts +20 -0
- package/dist/animate/waapi.d.ts.map +1 -0
- package/dist/animate/waapi.js +152 -0
- package/dist/animate/waapi.js.map +7 -0
- package/dist/dom.d.ts +12 -0
- package/dist/dom.d.ts.map +1 -0
- package/dist/dom.js +17 -0
- package/dist/dom.js.map +7 -0
- package/dist/easing/cubic-bezier.d.ts +4 -0
- package/dist/easing/cubic-bezier.d.ts.map +1 -0
- package/dist/easing/cubic-bezier.js +54 -0
- package/dist/easing/cubic-bezier.js.map +7 -0
- package/dist/easing/linear-css.d.ts +9 -0
- package/dist/easing/linear-css.d.ts.map +1 -0
- package/dist/easing/linear-css.js +30 -0
- package/dist/easing/linear-css.js.map +7 -0
- package/dist/easing/named.d.ts +16 -0
- package/dist/easing/named.d.ts.map +1 -0
- package/dist/easing/named.js +82 -0
- package/dist/easing/named.js.map +7 -0
- package/dist/easing/spring.d.ts +11 -0
- package/dist/easing/spring.d.ts.map +1 -0
- package/dist/easing/spring.js +18 -0
- package/dist/easing/spring.js.map +7 -0
- package/dist/easing/steps.d.ts +4 -0
- package/dist/easing/steps.d.ts.map +1 -0
- package/dist/easing/steps.js +14 -0
- package/dist/easing/steps.js.map +7 -0
- package/dist/easing/types.d.ts +5 -0
- package/dist/easing/types.d.ts.map +1 -0
- package/dist/easing/types.js +1 -0
- package/dist/easing/types.js.map +7 -0
- package/dist/frame/driver.d.ts +13 -0
- package/dist/frame/driver.d.ts.map +1 -0
- package/dist/frame/driver.js +42 -0
- package/dist/frame/driver.js.map +7 -0
- package/dist/frame/frame.d.ts +20 -0
- package/dist/frame/frame.d.ts.map +1 -0
- package/dist/frame/frame.js +77 -0
- package/dist/frame/frame.js.map +7 -0
- package/dist/generators/inertia.d.ts +20 -0
- package/dist/generators/inertia.d.ts.map +1 -0
- package/dist/generators/inertia.js +59 -0
- package/dist/generators/inertia.js.map +7 -0
- package/dist/generators/spring.d.ts +17 -0
- package/dist/generators/spring.d.ts.map +1 -0
- package/dist/generators/spring.js +87 -0
- package/dist/generators/spring.js.map +7 -0
- package/dist/generators/tween.d.ts +15 -0
- package/dist/generators/tween.d.ts.map +1 -0
- package/dist/generators/tween.js +51 -0
- package/dist/generators/tween.js.map +7 -0
- package/dist/generators/types.d.ts +30 -0
- package/dist/generators/types.d.ts.map +1 -0
- package/dist/generators/types.js +1 -0
- package/dist/generators/types.js.map +7 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +97 -0
- package/dist/index.js.map +7 -0
- package/dist/interpolate/array.d.ts +3 -0
- package/dist/interpolate/array.d.ts.map +1 -0
- package/dist/interpolate/array.js +12 -0
- package/dist/interpolate/array.js.map +7 -0
- package/dist/interpolate/color-parse.d.ts +14 -0
- package/dist/interpolate/color-parse.d.ts.map +1 -0
- package/dist/interpolate/color-parse.js +111 -0
- package/dist/interpolate/color-parse.js.map +7 -0
- package/dist/interpolate/color.d.ts +11 -0
- package/dist/interpolate/color.d.ts.map +1 -0
- package/dist/interpolate/color.js +74 -0
- package/dist/interpolate/color.js.map +7 -0
- package/dist/interpolate/complex.d.ts +7 -0
- package/dist/interpolate/complex.d.ts.map +1 -0
- package/dist/interpolate/complex.js +56 -0
- package/dist/interpolate/complex.js.map +7 -0
- package/dist/interpolate/interpolate.d.ts +16 -0
- package/dist/interpolate/interpolate.d.ts.map +1 -0
- package/dist/interpolate/interpolate.js +39 -0
- package/dist/interpolate/interpolate.js.map +7 -0
- package/dist/interpolate/mix.d.ts +8 -0
- package/dist/interpolate/mix.d.ts.map +1 -0
- package/dist/interpolate/mix.js +48 -0
- package/dist/interpolate/mix.js.map +7 -0
- package/dist/interpolate/number.d.ts +2 -0
- package/dist/interpolate/number.d.ts.map +1 -0
- package/dist/interpolate/number.js +7 -0
- package/dist/interpolate/number.js.map +7 -0
- package/dist/interpolate/object.d.ts +5 -0
- package/dist/interpolate/object.d.ts.map +1 -0
- package/dist/interpolate/object.js +17 -0
- package/dist/interpolate/object.js.map +7 -0
- package/dist/interpolate/path/flatten.d.ts +10 -0
- package/dist/interpolate/path/flatten.d.ts.map +1 -0
- package/dist/interpolate/path/flatten.js +135 -0
- package/dist/interpolate/path/flatten.js.map +7 -0
- package/dist/interpolate/path/morph.d.ts +9 -0
- package/dist/interpolate/path/morph.d.ts.map +1 -0
- package/dist/interpolate/path/morph.js +72 -0
- package/dist/interpolate/path/morph.js.map +7 -0
- package/dist/interpolate/path/parse.d.ts +24 -0
- package/dist/interpolate/path/parse.d.ts.map +1 -0
- package/dist/interpolate/path/parse.js +125 -0
- package/dist/interpolate/path/parse.js.map +7 -0
- package/dist/interpolate/path/rings.d.ts +13 -0
- package/dist/interpolate/path/rings.d.ts.map +1 -0
- package/dist/interpolate/path/rings.js +117 -0
- package/dist/interpolate/path/rings.js.map +7 -0
- package/dist/react/index.d.ts +7 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +19 -0
- package/dist/react/index.js.map +7 -0
- package/dist/react/presence.d.ts +27 -0
- package/dist/react/presence.d.ts.map +1 -0
- package/dist/react/presence.js +135 -0
- package/dist/react/presence.js.map +7 -0
- package/dist/react/use-animate.d.ts +17 -0
- package/dist/react/use-animate.d.ts.map +1 -0
- package/dist/react/use-animate.js +34 -0
- package/dist/react/use-animate.js.map +7 -0
- package/dist/react/use-motion-value.d.ts +4 -0
- package/dist/react/use-motion-value.d.ts.map +1 -0
- package/dist/react/use-motion-value.js +11 -0
- package/dist/react/use-motion-value.js.map +7 -0
- package/dist/react/use-reduced-motion.d.ts +7 -0
- package/dist/react/use-reduced-motion.d.ts.map +1 -0
- package/dist/react/use-reduced-motion.js +19 -0
- package/dist/react/use-reduced-motion.js.map +7 -0
- package/dist/react/use-spring.d.ts +9 -0
- package/dist/react/use-spring.d.ts.map +1 -0
- package/dist/react/use-spring.js +27 -0
- package/dist/react/use-spring.js.map +7 -0
- package/dist/react/use-transform.d.ts +13 -0
- package/dist/react/use-transform.d.ts.map +1 -0
- package/dist/react/use-transform.js +44 -0
- package/dist/react/use-transform.js.map +7 -0
- package/dist/sequence/sequence.d.ts +25 -0
- package/dist/sequence/sequence.d.ts.map +1 -0
- package/dist/sequence/sequence.js +68 -0
- package/dist/sequence/sequence.js.map +7 -0
- package/dist/stagger/stagger.d.ts +12 -0
- package/dist/stagger/stagger.d.ts.map +1 -0
- package/dist/stagger/stagger.js +21 -0
- package/dist/stagger/stagger.js.map +7 -0
- package/dist/testing/manual-frames.d.ts +11 -0
- package/dist/testing/manual-frames.d.ts.map +1 -0
- package/dist/testing/manual-frames.js +40 -0
- package/dist/testing/manual-frames.js.map +7 -0
- package/dist/value/motion-value.d.ts +27 -0
- package/dist/value/motion-value.d.ts.map +1 -0
- package/dist/value/motion-value.js +88 -0
- package/dist/value/motion-value.js.map +7 -0
- package/dist/value/transform.d.ts +16 -0
- package/dist/value/transform.d.ts.map +1 -0
- package/dist/value/transform.js +28 -0
- package/dist/value/transform.js.map +7 -0
- package/dist/view/in-view.d.ts +19 -0
- package/dist/view/in-view.d.ts.map +1 -0
- package/dist/view/in-view.js +46 -0
- package/dist/view/in-view.js.map +7 -0
- package/dist/view/resize.d.ts +13 -0
- package/dist/view/resize.d.ts.map +1 -0
- package/dist/view/resize.js +72 -0
- package/dist/view/resize.js.map +7 -0
- package/dist/view/resolve-elements.d.ts +3 -0
- package/dist/view/resolve-elements.d.ts.map +1 -0
- package/dist/view/resolve-elements.js +10 -0
- package/dist/view/resolve-elements.js.map +7 -0
- package/dist/view/scroll-measure.d.ts +16 -0
- package/dist/view/scroll-measure.d.ts.map +1 -0
- package/dist/view/scroll-measure.js +51 -0
- package/dist/view/scroll-measure.js.map +7 -0
- package/dist/view/scroll-offset.d.ts +28 -0
- package/dist/view/scroll-offset.d.ts.map +1 -0
- package/dist/view/scroll-offset.js +55 -0
- package/dist/view/scroll-offset.js.map +7 -0
- package/dist/view/scroll.d.ts +34 -0
- package/dist/view/scroll.d.ts.map +1 -0
- package/dist/view/scroll.js +83 -0
- package/dist/view/scroll.js.map +7 -0
- package/package.json +57 -5
- package/src/accessibility/reduced-motion.ts +75 -0
- package/src/animate/animate.ts +155 -0
- package/src/animate/controls.ts +76 -0
- package/src/animate/dom.ts +166 -0
- package/src/animate/js-animation.ts +151 -0
- package/src/animate/plan.ts +74 -0
- package/src/animate/style.ts +131 -0
- package/src/animate/types.ts +69 -0
- package/src/animate/value.ts +93 -0
- package/src/animate/waapi.ts +212 -0
- package/src/dom.ts +35 -0
- package/src/easing/cubic-bezier.ts +64 -0
- package/src/easing/linear-css.ts +41 -0
- package/src/easing/named.ts +88 -0
- package/src/easing/spring.ts +25 -0
- package/src/easing/steps.ts +16 -0
- package/src/easing/types.ts +18 -0
- package/src/frame/driver.ts +56 -0
- package/src/frame/frame.ts +102 -0
- package/src/generators/inertia.ts +88 -0
- package/src/generators/spring.ts +117 -0
- package/src/generators/tween.ts +74 -0
- package/src/generators/types.ts +31 -0
- package/src/index.ts +91 -0
- package/src/interpolate/array.ts +13 -0
- package/src/interpolate/color-parse.ts +136 -0
- package/src/interpolate/color.ts +97 -0
- package/src/interpolate/complex.ts +80 -0
- package/src/interpolate/interpolate.ts +60 -0
- package/src/interpolate/mix.ts +74 -0
- package/src/interpolate/number.ts +3 -0
- package/src/interpolate/object.ts +20 -0
- package/src/interpolate/path/flatten.ts +185 -0
- package/src/interpolate/path/morph.ts +100 -0
- package/src/interpolate/path/parse.ts +162 -0
- package/src/interpolate/path/rings.ts +151 -0
- package/src/react/index.ts +11 -0
- package/src/react/presence.tsx +194 -0
- package/src/react/use-animate.ts +46 -0
- package/src/react/use-motion-value.ts +9 -0
- package/src/react/use-reduced-motion.ts +22 -0
- package/src/react/use-spring.ts +35 -0
- package/src/react/use-transform.ts +73 -0
- package/src/sequence/sequence.ts +129 -0
- package/src/stagger/stagger.ts +36 -0
- package/src/testing/browser-commands.d.ts +11 -0
- package/src/testing/manual-frames.ts +49 -0
- package/src/value/motion-value.ts +131 -0
- package/src/value/transform.ts +61 -0
- package/src/view/in-view.ts +74 -0
- package/src/view/resize.ts +101 -0
- package/src/view/resolve-elements.ts +11 -0
- package/src/view/scroll-measure.ts +83 -0
- package/src/view/scroll-offset.ts +100 -0
- package/src/view/scroll.ts +143 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { groupControls, thenable } from "../animate/controls.js";
|
|
2
|
+
import { now } from "../frame/frame.js";
|
|
3
|
+
function startOf(at, cursor, previousStart, labels) {
|
|
4
|
+
if (at === void 0) return cursor;
|
|
5
|
+
if (typeof at === "number") return at;
|
|
6
|
+
if (at === "<") return previousStart;
|
|
7
|
+
if (/^[+-]/.test(at)) return Math.max(0, cursor + Number(at));
|
|
8
|
+
return labels.get(at) ?? cursor;
|
|
9
|
+
}
|
|
10
|
+
function animateSequence(segments, options, animateOne, resolveTargets) {
|
|
11
|
+
const begun = now();
|
|
12
|
+
const labels = /* @__PURE__ */ new Map();
|
|
13
|
+
const busy = /* @__PURE__ */ new Map();
|
|
14
|
+
const children = [];
|
|
15
|
+
const waits = [];
|
|
16
|
+
let cursor = 0;
|
|
17
|
+
let previousStart = 0;
|
|
18
|
+
for (const segment of segments) {
|
|
19
|
+
let optionsAt2 = function(elapsed) {
|
|
20
|
+
const offset = start - elapsed;
|
|
21
|
+
return {
|
|
22
|
+
...options,
|
|
23
|
+
...own,
|
|
24
|
+
delay: typeof delay === "function" ? (index, total) => Math.max(0, offset + delay(index, total)) : Math.max(0, offset + (delay ?? 0))
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
var optionsAt = optionsAt2;
|
|
28
|
+
if (typeof segment === "string" || "name" in segment) {
|
|
29
|
+
const label = typeof segment === "string" ? { name: segment } : segment;
|
|
30
|
+
labels.set(label.name, startOf(label.at, cursor, previousStart, labels));
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const [target, keyframes, own = {}] = segment;
|
|
34
|
+
const start = startOf(own.at, cursor, previousStart, labels);
|
|
35
|
+
const { delay } = own;
|
|
36
|
+
const keys = typeof keyframes === "object" && keyframes !== null && !Array.isArray(keyframes) ? Object.keys(keyframes) : ["value"];
|
|
37
|
+
const targets = resolveTargets(target);
|
|
38
|
+
const before = targets.flatMap(
|
|
39
|
+
(one) => keys.flatMap((key) => busy.get(one)?.get(key) ?? [])
|
|
40
|
+
);
|
|
41
|
+
const launch = () => animateOne(target, keyframes, optionsAt2((now() - begun) / 1e3));
|
|
42
|
+
let finished2;
|
|
43
|
+
if (before.length === 0) {
|
|
44
|
+
const controls = launch();
|
|
45
|
+
children.push(controls);
|
|
46
|
+
finished2 = controls.finished;
|
|
47
|
+
cursor = Math.max(start, controls.duration);
|
|
48
|
+
} else {
|
|
49
|
+
finished2 = Promise.all(before).then(() => launch().finished);
|
|
50
|
+
waits.push(finished2);
|
|
51
|
+
}
|
|
52
|
+
for (const one of targets) {
|
|
53
|
+
const byKey = busy.get(one) ?? /* @__PURE__ */ new Map();
|
|
54
|
+
busy.set(one, byKey);
|
|
55
|
+
for (const key of keys) byKey.set(key, finished2);
|
|
56
|
+
}
|
|
57
|
+
previousStart = start;
|
|
58
|
+
}
|
|
59
|
+
const group = groupControls(children);
|
|
60
|
+
const finished = Promise.all([group.finished, ...waits]).then(
|
|
61
|
+
() => void 0
|
|
62
|
+
);
|
|
63
|
+
return Object.assign(group, thenable(finished));
|
|
64
|
+
}
|
|
65
|
+
export {
|
|
66
|
+
animateSequence
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=sequence.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/sequence/sequence.ts"],
|
|
4
|
+
"sourcesContent": ["import { groupControls, thenable } from \"#/animate/controls.ts\";\nimport type {\n\tAnimationControls,\n\tAnimationOptions,\n\tDOMKeyframes,\n\tElementTarget,\n\tKeyframes,\n} from \"#/animate/types.ts\";\nimport { now } from \"#/frame/frame.ts\";\nimport type { MotionValue } from \"#/value/motion-value.ts\";\n\n/**\n * When a segment starts: seconds from the start of the sequence, `\"+0.1\"` or\n * `\"-0.2\"` from the end of the segment before, `\"<\"` with the segment before,\n * or the name of a label.\n */\nexport type SequenceAt = number | `+${number}` | `-${number}` | \"<\" | string;\n\nexport interface SegmentOptions extends AnimationOptions {\n\treadonly at?: SequenceAt;\n}\n\nexport type Segment =\n\t| readonly [ElementTarget, DOMKeyframes, SegmentOptions?]\n\t// biome-ignore lint/suspicious/noExplicitAny: a sequence mixes values of any type.\n\t| readonly [MotionValue<any>, Keyframes<any>, SegmentOptions?]\n\t| string\n\t| { readonly name: string; readonly at?: SequenceAt };\n\nexport type AnimateOne = (\n\ttarget: unknown,\n\tkeyframes: unknown,\n\toptions: AnimationOptions,\n) => AnimationControls;\n\nexport type ResolveTargets = (target: unknown) => readonly object[];\n\nfunction startOf(\n\tat: SequenceAt | undefined,\n\tcursor: number,\n\tpreviousStart: number,\n\tlabels: ReadonlyMap<string, number>,\n): number {\n\tif (at === undefined) return cursor;\n\tif (typeof at === \"number\") return at;\n\tif (at === \"<\") return previousStart;\n\tif (/^[+-]/.test(at)) return Math.max(0, cursor + Number(at));\n\treturn labels.get(at) ?? cursor;\n}\n\n/**\n * Launches every segment at once, each delayed to its place on the\n * timeline, so the whole sequence shares one clock and can be scrubbed. A\n * segment that animates a property an earlier segment already animates\n * waits for that one to finish, then starts from where it left off.\n */\nexport function animateSequence(\n\tsegments: readonly Segment[],\n\toptions: AnimationOptions,\n\tanimateOne: AnimateOne,\n\tresolveTargets: ResolveTargets,\n): AnimationControls {\n\tconst begun = now();\n\tconst labels = new Map<string, number>();\n\tconst busy = new Map<object, Map<string, Promise<void>>>();\n\tconst children: AnimationControls[] = [];\n\tconst waits: Promise<void>[] = [];\n\tlet cursor = 0;\n\tlet previousStart = 0;\n\tfor (const segment of segments) {\n\t\tif (typeof segment === \"string\" || \"name\" in segment) {\n\t\t\tconst label = typeof segment === \"string\" ? { name: segment } : segment;\n\t\t\tlabels.set(label.name, startOf(label.at, cursor, previousStart, labels));\n\t\t\tcontinue;\n\t\t}\n\t\tconst [target, keyframes, own = {}] = segment as readonly [\n\t\t\tunknown,\n\t\t\tunknown,\n\t\t\tSegmentOptions?,\n\t\t];\n\t\tconst start = startOf(own.at, cursor, previousStart, labels);\n\t\tconst { delay } = own;\n\t\t/** Launched late, a segment only waits for what is left of its delay. */\n\t\tfunction optionsAt(elapsed: number): AnimationOptions {\n\t\t\tconst offset = start - elapsed;\n\t\t\treturn {\n\t\t\t\t...options,\n\t\t\t\t...own,\n\t\t\t\tdelay:\n\t\t\t\t\ttypeof delay === \"function\"\n\t\t\t\t\t\t? (index, total) => Math.max(0, offset + delay(index, total))\n\t\t\t\t\t\t: Math.max(0, offset + (delay ?? 0)),\n\t\t\t};\n\t\t}\n\t\tconst keys =\n\t\t\ttypeof keyframes === \"object\" &&\n\t\t\tkeyframes !== null &&\n\t\t\t!Array.isArray(keyframes)\n\t\t\t\t? Object.keys(keyframes)\n\t\t\t\t: [\"value\"];\n\t\tconst targets = resolveTargets(target);\n\t\tconst before = targets.flatMap((one) =>\n\t\t\tkeys.flatMap((key) => busy.get(one)?.get(key) ?? []),\n\t\t);\n\t\tconst launch = () =>\n\t\t\tanimateOne(target, keyframes, optionsAt((now() - begun) / 1000));\n\t\tlet finished: Promise<void>;\n\t\tif (before.length === 0) {\n\t\t\tconst controls = launch();\n\t\t\tchildren.push(controls);\n\t\t\tfinished = controls.finished;\n\t\t\tcursor = Math.max(start, controls.duration);\n\t\t} else {\n\t\t\tfinished = Promise.all(before).then(() => launch().finished);\n\t\t\twaits.push(finished);\n\t\t}\n\t\tfor (const one of targets) {\n\t\t\tconst byKey = busy.get(one) ?? new Map();\n\t\t\tbusy.set(one, byKey);\n\t\t\tfor (const key of keys) byKey.set(key, finished);\n\t\t}\n\t\tpreviousStart = start;\n\t}\n\tconst group = groupControls(children);\n\tconst finished = Promise.all([group.finished, ...waits]).then(\n\t\t() => undefined,\n\t);\n\treturn Object.assign(group, thenable(finished));\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,eAAe,gBAAgB;AAQxC,SAAS,WAAW;AA6BpB,SAAS,QACR,IACA,QACA,eACA,QACS;AACT,MAAI,OAAO,OAAW,QAAO;AAC7B,MAAI,OAAO,OAAO,SAAU,QAAO;AACnC,MAAI,OAAO,IAAK,QAAO;AACvB,MAAI,QAAQ,KAAK,EAAE,EAAG,QAAO,KAAK,IAAI,GAAG,SAAS,OAAO,EAAE,CAAC;AAC5D,SAAO,OAAO,IAAI,EAAE,KAAK;AAC1B;AAQO,SAAS,gBACf,UACA,SACA,YACA,gBACoB;AACpB,QAAM,QAAQ,IAAI;AAClB,QAAM,SAAS,oBAAI,IAAoB;AACvC,QAAM,OAAO,oBAAI,IAAwC;AACzD,QAAM,WAAgC,CAAC;AACvC,QAAM,QAAyB,CAAC;AAChC,MAAI,SAAS;AACb,MAAI,gBAAgB;AACpB,aAAW,WAAW,UAAU;AAc/B,QAASA,aAAT,SAAmB,SAAmC;AACrD,YAAM,SAAS,QAAQ;AACvB,aAAO;AAAA,QACN,GAAG;AAAA,QACH,GAAG;AAAA,QACH,OACC,OAAO,UAAU,aACd,CAAC,OAAO,UAAU,KAAK,IAAI,GAAG,SAAS,MAAM,OAAO,KAAK,CAAC,IAC1D,KAAK,IAAI,GAAG,UAAU,SAAS,EAAE;AAAA,MACtC;AAAA,IACD;AAVS,oBAAAA;AAbT,QAAI,OAAO,YAAY,YAAY,UAAU,SAAS;AACrD,YAAM,QAAQ,OAAO,YAAY,WAAW,EAAE,MAAM,QAAQ,IAAI;AAChE,aAAO,IAAI,MAAM,MAAM,QAAQ,MAAM,IAAI,QAAQ,eAAe,MAAM,CAAC;AACvE;AAAA,IACD;AACA,UAAM,CAAC,QAAQ,WAAW,MAAM,CAAC,CAAC,IAAI;AAKtC,UAAM,QAAQ,QAAQ,IAAI,IAAI,QAAQ,eAAe,MAAM;AAC3D,UAAM,EAAE,MAAM,IAAI;AAalB,UAAM,OACL,OAAO,cAAc,YACrB,cAAc,QACd,CAAC,MAAM,QAAQ,SAAS,IACrB,OAAO,KAAK,SAAS,IACrB,CAAC,OAAO;AACZ,UAAM,UAAU,eAAe,MAAM;AACrC,UAAM,SAAS,QAAQ;AAAA,MAAQ,CAAC,QAC/B,KAAK,QAAQ,CAAC,QAAQ,KAAK,IAAI,GAAG,GAAG,IAAI,GAAG,KAAK,CAAC,CAAC;AAAA,IACpD;AACA,UAAM,SAAS,MACd,WAAW,QAAQ,WAAWA,YAAW,IAAI,IAAI,SAAS,GAAI,CAAC;AAChE,QAAIC;AACJ,QAAI,OAAO,WAAW,GAAG;AACxB,YAAM,WAAW,OAAO;AACxB,eAAS,KAAK,QAAQ;AACtB,MAAAA,YAAW,SAAS;AACpB,eAAS,KAAK,IAAI,OAAO,SAAS,QAAQ;AAAA,IAC3C,OAAO;AACN,MAAAA,YAAW,QAAQ,IAAI,MAAM,EAAE,KAAK,MAAM,OAAO,EAAE,QAAQ;AAC3D,YAAM,KAAKA,SAAQ;AAAA,IACpB;AACA,eAAW,OAAO,SAAS;AAC1B,YAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,oBAAI,IAAI;AACvC,WAAK,IAAI,KAAK,KAAK;AACnB,iBAAW,OAAO,KAAM,OAAM,IAAI,KAAKA,SAAQ;AAAA,IAChD;AACA,oBAAgB;AAAA,EACjB;AACA,QAAM,QAAQ,cAAc,QAAQ;AACpC,QAAM,WAAW,QAAQ,IAAI,CAAC,MAAM,UAAU,GAAG,KAAK,CAAC,EAAE;AAAA,IACxD,MAAM;AAAA,EACP;AACA,SAAO,OAAO,OAAO,OAAO,SAAS,QAAQ,CAAC;AAC/C;",
|
|
6
|
+
"names": ["optionsAt", "finished"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { EasingDefinition } from "../easing/types.js";
|
|
2
|
+
export interface StaggerOptions {
|
|
3
|
+
/** Seconds before the first one starts. */
|
|
4
|
+
readonly startDelay?: number;
|
|
5
|
+
/** Which one starts first; the others follow by their distance from it. */
|
|
6
|
+
readonly from?: "first" | "last" | "center" | number;
|
|
7
|
+
/** Spreads the delays along an easing instead of evenly. */
|
|
8
|
+
readonly ease?: EasingDefinition;
|
|
9
|
+
}
|
|
10
|
+
/** A `delay` for many targets: `gap` seconds between neighbours. */
|
|
11
|
+
export declare function stagger(gap?: number, options?: StaggerOptions): (index: number, total: number) => number;
|
|
12
|
+
//# sourceMappingURL=stagger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stagger.d.ts","sourceRoot":"","sources":["../../src/stagger/stagger.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,MAAM,WAAW,cAAc;IAC9B,2CAA2C;IAC3C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;IACrD,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACjC;AASD,oEAAoE;AACpE,wBAAgB,OAAO,CACtB,GAAG,SAAM,EACT,OAAO,GAAE,cAAmB,GAC1B,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAY1C"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { resolveEasing } from "../easing/named.js";
|
|
2
|
+
const ORIGINS = {
|
|
3
|
+
first: () => 0,
|
|
4
|
+
last: (total) => total - 1,
|
|
5
|
+
center: (total) => (total - 1) / 2
|
|
6
|
+
};
|
|
7
|
+
function stagger(gap = 0.1, options = {}) {
|
|
8
|
+
const { startDelay = 0, from = "first", ease } = options;
|
|
9
|
+
return (index, total) => {
|
|
10
|
+
const origin = typeof from === "number" ? from : ORIGINS[from](total);
|
|
11
|
+
const distance = Math.abs(index - origin);
|
|
12
|
+
if (ease === void 0) return startDelay + distance * gap;
|
|
13
|
+
const furthest = Math.max(origin, total - 1 - origin);
|
|
14
|
+
const spread = furthest * gap;
|
|
15
|
+
return furthest === 0 ? startDelay : startDelay + resolveEasing(ease)(distance / furthest) * spread;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export {
|
|
19
|
+
stagger
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=stagger.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/stagger/stagger.ts"],
|
|
4
|
+
"sourcesContent": ["import { resolveEasing } from \"#/easing/named.ts\";\nimport type { EasingDefinition } from \"#/easing/types.ts\";\n\nexport interface StaggerOptions {\n\t/** Seconds before the first one starts. */\n\treadonly startDelay?: number;\n\t/** Which one starts first; the others follow by their distance from it. */\n\treadonly from?: \"first\" | \"last\" | \"center\" | number;\n\t/** Spreads the delays along an easing instead of evenly. */\n\treadonly ease?: EasingDefinition;\n}\n\nconst ORIGINS: Record<\"first\" | \"last\" | \"center\", (total: number) => number> =\n\t{\n\t\tfirst: () => 0,\n\t\tlast: (total) => total - 1,\n\t\tcenter: (total) => (total - 1) / 2,\n\t};\n\n/** A `delay` for many targets: `gap` seconds between neighbours. */\nexport function stagger(\n\tgap = 0.1,\n\toptions: StaggerOptions = {},\n): (index: number, total: number) => number {\n\tconst { startDelay = 0, from = \"first\", ease } = options;\n\treturn (index, total) => {\n\t\tconst origin = typeof from === \"number\" ? from : ORIGINS[from](total);\n\t\tconst distance = Math.abs(index - origin);\n\t\tif (ease === undefined) return startDelay + distance * gap;\n\t\tconst furthest = Math.max(origin, total - 1 - origin);\n\t\tconst spread = furthest * gap;\n\t\treturn furthest === 0\n\t\t\t? startDelay\n\t\t\t: startDelay + resolveEasing(ease)(distance / furthest) * spread;\n\t};\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,qBAAqB;AAY9B,MAAM,UACL;AAAA,EACC,OAAO,MAAM;AAAA,EACb,MAAM,CAAC,UAAU,QAAQ;AAAA,EACzB,QAAQ,CAAC,WAAW,QAAQ,KAAK;AAClC;AAGM,SAAS,QACf,MAAM,KACN,UAA0B,CAAC,GACgB;AAC3C,QAAM,EAAE,aAAa,GAAG,OAAO,SAAS,KAAK,IAAI;AACjD,SAAO,CAAC,OAAO,UAAU;AACxB,UAAM,SAAS,OAAO,SAAS,WAAW,OAAO,QAAQ,IAAI,EAAE,KAAK;AACpE,UAAM,WAAW,KAAK,IAAI,QAAQ,MAAM;AACxC,QAAI,SAAS,OAAW,QAAO,aAAa,WAAW;AACvD,UAAM,WAAW,KAAK,IAAI,QAAQ,QAAQ,IAAI,MAAM;AACpD,UAAM,SAAS,WAAW;AAC1B,WAAO,aAAa,IACjB,aACA,aAAa,cAAc,IAAI,EAAE,WAAW,QAAQ,IAAI;AAAA,EAC5D;AACD;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface ManualFrames {
|
|
2
|
+
/** Moves the clock by `ms`, running each 60 Hz frame it crosses while one is requested. */
|
|
3
|
+
advance(ms: number): void;
|
|
4
|
+
/** The clock, in milliseconds. */
|
|
5
|
+
time(): number;
|
|
6
|
+
/** Back to the browser's frames, with an empty loop. */
|
|
7
|
+
restore(): void;
|
|
8
|
+
}
|
|
9
|
+
/** Installs a clock the test steps by hand, starting at 0 with an empty loop. */
|
|
10
|
+
export declare function manualFrames(): ManualFrames;
|
|
11
|
+
//# sourceMappingURL=manual-frames.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manual-frames.d.ts","sourceRoot":"","sources":["../../src/testing/manual-frames.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,YAAY;IAC5B,2FAA2F;IAC3F,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,kCAAkC;IAClC,IAAI,IAAI,MAAM,CAAC;IACf,wDAAwD;IACxD,OAAO,IAAI,IAAI,CAAC;CAChB;AAED,iFAAiF;AACjF,wBAAgB,YAAY,IAAI,YAAY,CAiC3C"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { setFrameDriver } from "../frame/driver.js";
|
|
2
|
+
import { resetFrameLoop } from "../frame/frame.js";
|
|
3
|
+
const FRAME = 1e3 / 60;
|
|
4
|
+
function manualFrames() {
|
|
5
|
+
let time = 0;
|
|
6
|
+
let tick;
|
|
7
|
+
resetFrameLoop();
|
|
8
|
+
setFrameDriver({
|
|
9
|
+
request(next) {
|
|
10
|
+
tick = next;
|
|
11
|
+
},
|
|
12
|
+
cancel() {
|
|
13
|
+
tick = void 0;
|
|
14
|
+
},
|
|
15
|
+
now: () => time
|
|
16
|
+
});
|
|
17
|
+
return {
|
|
18
|
+
advance(ms) {
|
|
19
|
+
const end = time + ms;
|
|
20
|
+
let frameAt = (Math.floor(time / FRAME + 1e-9) + 1) * FRAME;
|
|
21
|
+
while (frameAt <= end + 1e-9) {
|
|
22
|
+
time = frameAt;
|
|
23
|
+
const next = tick;
|
|
24
|
+
tick = void 0;
|
|
25
|
+
next?.(time);
|
|
26
|
+
frameAt += FRAME;
|
|
27
|
+
}
|
|
28
|
+
time = end;
|
|
29
|
+
},
|
|
30
|
+
time: () => time,
|
|
31
|
+
restore() {
|
|
32
|
+
resetFrameLoop();
|
|
33
|
+
setFrameDriver(void 0);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export {
|
|
38
|
+
manualFrames
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=manual-frames.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/testing/manual-frames.ts"],
|
|
4
|
+
"sourcesContent": ["import { setFrameDriver } from \"#/frame/driver.ts\";\nimport { resetFrameLoop } from \"#/frame/frame.ts\";\n\nconst FRAME = 1000 / 60;\n\nexport interface ManualFrames {\n\t/** Moves the clock by `ms`, running each 60 Hz frame it crosses while one is requested. */\n\tadvance(ms: number): void;\n\t/** The clock, in milliseconds. */\n\ttime(): number;\n\t/** Back to the browser's frames, with an empty loop. */\n\trestore(): void;\n}\n\n/** Installs a clock the test steps by hand, starting at 0 with an empty loop. */\nexport function manualFrames(): ManualFrames {\n\tlet time = 0;\n\tlet tick: ((timestamp: number) => void) | undefined;\n\tresetFrameLoop();\n\tsetFrameDriver({\n\t\trequest(next) {\n\t\t\ttick = next;\n\t\t},\n\t\tcancel() {\n\t\t\ttick = undefined;\n\t\t},\n\t\tnow: () => time,\n\t});\n\treturn {\n\t\tadvance(ms) {\n\t\t\tconst end = time + ms;\n\t\t\t// Frames land on the 60 Hz grid however the test slices its steps.\n\t\t\tlet frameAt = (Math.floor(time / FRAME + 1e-9) + 1) * FRAME;\n\t\t\twhile (frameAt <= end + 1e-9) {\n\t\t\t\ttime = frameAt;\n\t\t\t\tconst next = tick;\n\t\t\t\ttick = undefined;\n\t\t\t\tnext?.(time);\n\t\t\t\tframeAt += FRAME;\n\t\t\t}\n\t\t\ttime = end;\n\t\t},\n\t\ttime: () => time,\n\t\trestore() {\n\t\t\tresetFrameLoop();\n\t\t\tsetFrameDriver(undefined);\n\t\t},\n\t};\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,sBAAsB;AAC/B,SAAS,sBAAsB;AAE/B,MAAM,QAAQ,MAAO;AAYd,SAAS,eAA6B;AAC5C,MAAI,OAAO;AACX,MAAI;AACJ,iBAAe;AACf,iBAAe;AAAA,IACd,QAAQ,MAAM;AACb,aAAO;AAAA,IACR;AAAA,IACA,SAAS;AACR,aAAO;AAAA,IACR;AAAA,IACA,KAAK,MAAM;AAAA,EACZ,CAAC;AACD,SAAO;AAAA,IACN,QAAQ,IAAI;AACX,YAAM,MAAM,OAAO;AAEnB,UAAI,WAAW,KAAK,MAAM,OAAO,QAAQ,IAAI,IAAI,KAAK;AACtD,aAAO,WAAW,MAAM,MAAM;AAC7B,eAAO;AACP,cAAM,OAAO;AACb,eAAO;AACP,eAAO,IAAI;AACX,mBAAW;AAAA,MACZ;AACA,aAAO;AAAA,IACR;AAAA,IACA,MAAM,MAAM;AAAA,IACZ,UAAU;AACT,qBAAe;AACf,qBAAe,MAAS;AAAA,IACzB;AAAA,EACD;AACD;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type MotionValueEvent = "change" | "animationStart" | "animationComplete" | "animationCancel";
|
|
2
|
+
/** What `animate()` hands a value it drives; `finished` lets the value announce the end. */
|
|
3
|
+
export interface MotionValueAnimation {
|
|
4
|
+
stop(): void;
|
|
5
|
+
readonly finished?: PromiseLike<unknown>;
|
|
6
|
+
}
|
|
7
|
+
export interface MotionValue<T = number> {
|
|
8
|
+
get(): T;
|
|
9
|
+
/** Sets the value and tracks its velocity. */
|
|
10
|
+
set(value: T): void;
|
|
11
|
+
/** Sets the value with no velocity and stops any animation. */
|
|
12
|
+
jump(value: T): void;
|
|
13
|
+
/** Units per second; 0 for a non-number or a value left alone for 30 ms. */
|
|
14
|
+
getVelocity(): number;
|
|
15
|
+
getPrevious(): T | undefined;
|
|
16
|
+
on(event: "change", listener: (value: T) => void): () => void;
|
|
17
|
+
on(event: Exclude<MotionValueEvent, "change">, listener: () => void): () => void;
|
|
18
|
+
/** Stops the running animation (`animationCancel`), then `animationStart`. */
|
|
19
|
+
start(animation: MotionValueAnimation): void;
|
|
20
|
+
/** Stops the running animation, if any, with `animationCancel`. */
|
|
21
|
+
stop(): void;
|
|
22
|
+
isAnimating(): boolean;
|
|
23
|
+
destroy(): void;
|
|
24
|
+
}
|
|
25
|
+
export declare function isMotionValue(value: unknown): value is MotionValue<unknown>;
|
|
26
|
+
export declare function motionValue<T>(initial: T): MotionValue<T>;
|
|
27
|
+
//# sourceMappingURL=motion-value.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"motion-value.d.ts","sourceRoot":"","sources":["../../src/value/motion-value.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,gBAAgB,GACzB,QAAQ,GACR,gBAAgB,GAChB,mBAAmB,GACnB,iBAAiB,CAAC;AAErB,4FAA4F;AAC5F,MAAM,WAAW,oBAAoB;IACpC,IAAI,IAAI,IAAI,CAAC;IACb,QAAQ,CAAC,QAAQ,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,MAAM;IACtC,GAAG,IAAI,CAAC,CAAC;IACT,8CAA8C;IAC9C,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IACpB,+DAA+D;IAC/D,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IACrB,4EAA4E;IAC5E,WAAW,IAAI,MAAM,CAAC;IACtB,WAAW,IAAI,CAAC,GAAG,SAAS,CAAC;IAC7B,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAC9D,EAAE,CACD,KAAK,EAAE,OAAO,CAAC,gBAAgB,EAAE,QAAQ,CAAC,EAC1C,QAAQ,EAAE,MAAM,IAAI,GAClB,MAAM,IAAI,CAAC;IACd,8EAA8E;IAC9E,KAAK,CAAC,SAAS,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAC7C,mEAAmE;IACnE,IAAI,IAAI,IAAI,CAAC;IACb,WAAW,IAAI,OAAO,CAAC;IACvB,OAAO,IAAI,IAAI,CAAC;CAChB;AAKD,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAAC,OAAO,CAAC,CAE3E;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAuFzD"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { now } from "../frame/frame.js";
|
|
2
|
+
const STALE_AFTER = 30;
|
|
3
|
+
const values = /* @__PURE__ */ new WeakSet();
|
|
4
|
+
function isMotionValue(value) {
|
|
5
|
+
return typeof value === "object" && value !== null && values.has(value);
|
|
6
|
+
}
|
|
7
|
+
function motionValue(initial) {
|
|
8
|
+
let current = initial;
|
|
9
|
+
let updatedAt = now();
|
|
10
|
+
let last;
|
|
11
|
+
let previous;
|
|
12
|
+
let previousAt = updatedAt;
|
|
13
|
+
let animation;
|
|
14
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
15
|
+
function emit(event) {
|
|
16
|
+
for (const listener of listeners.get(event) ?? []) listener(current);
|
|
17
|
+
}
|
|
18
|
+
function stop() {
|
|
19
|
+
const running = animation;
|
|
20
|
+
if (!running) return;
|
|
21
|
+
animation = void 0;
|
|
22
|
+
running.stop();
|
|
23
|
+
emit("animationCancel");
|
|
24
|
+
}
|
|
25
|
+
const value = {
|
|
26
|
+
get: () => current,
|
|
27
|
+
set(next) {
|
|
28
|
+
const time = now();
|
|
29
|
+
if (time !== updatedAt) {
|
|
30
|
+
previous = current;
|
|
31
|
+
previousAt = updatedAt;
|
|
32
|
+
}
|
|
33
|
+
last = current;
|
|
34
|
+
current = next;
|
|
35
|
+
updatedAt = time;
|
|
36
|
+
emit("change");
|
|
37
|
+
},
|
|
38
|
+
jump(next) {
|
|
39
|
+
stop();
|
|
40
|
+
last = current;
|
|
41
|
+
current = next;
|
|
42
|
+
previous = void 0;
|
|
43
|
+
updatedAt = now();
|
|
44
|
+
previousAt = updatedAt;
|
|
45
|
+
emit("change");
|
|
46
|
+
},
|
|
47
|
+
getVelocity() {
|
|
48
|
+
const elapsed = updatedAt - previousAt;
|
|
49
|
+
if (typeof current !== "number" || typeof previous !== "number" || elapsed <= 0 || now() - updatedAt > STALE_AFTER) {
|
|
50
|
+
return 0;
|
|
51
|
+
}
|
|
52
|
+
return (current - previous) / elapsed * 1e3;
|
|
53
|
+
},
|
|
54
|
+
getPrevious: () => last,
|
|
55
|
+
// One implementation behind both overloads: a lifecycle listener ignores the argument.
|
|
56
|
+
on: ((event, listener) => {
|
|
57
|
+
const set = listeners.get(event) ?? /* @__PURE__ */ new Set();
|
|
58
|
+
listeners.set(event, set);
|
|
59
|
+
set.add(listener);
|
|
60
|
+
return () => {
|
|
61
|
+
set.delete(listener);
|
|
62
|
+
};
|
|
63
|
+
}),
|
|
64
|
+
start(next) {
|
|
65
|
+
stop();
|
|
66
|
+
animation = next;
|
|
67
|
+
emit("animationStart");
|
|
68
|
+
next.finished?.then(() => {
|
|
69
|
+
if (animation !== next) return;
|
|
70
|
+
animation = void 0;
|
|
71
|
+
emit("animationComplete");
|
|
72
|
+
});
|
|
73
|
+
},
|
|
74
|
+
stop,
|
|
75
|
+
isAnimating: () => animation !== void 0,
|
|
76
|
+
destroy() {
|
|
77
|
+
stop();
|
|
78
|
+
listeners.clear();
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
values.add(value);
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
export {
|
|
85
|
+
isMotionValue,
|
|
86
|
+
motionValue
|
|
87
|
+
};
|
|
88
|
+
//# sourceMappingURL=motion-value.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/value/motion-value.ts"],
|
|
4
|
+
"sourcesContent": ["import { now } from \"#/frame/frame.ts\";\n\nexport type MotionValueEvent =\n\t| \"change\"\n\t| \"animationStart\"\n\t| \"animationComplete\"\n\t| \"animationCancel\";\n\n/** What `animate()` hands a value it drives; `finished` lets the value announce the end. */\nexport interface MotionValueAnimation {\n\tstop(): void;\n\treadonly finished?: PromiseLike<unknown>;\n}\n\nexport interface MotionValue<T = number> {\n\tget(): T;\n\t/** Sets the value and tracks its velocity. */\n\tset(value: T): void;\n\t/** Sets the value with no velocity and stops any animation. */\n\tjump(value: T): void;\n\t/** Units per second; 0 for a non-number or a value left alone for 30 ms. */\n\tgetVelocity(): number;\n\tgetPrevious(): T | undefined;\n\ton(event: \"change\", listener: (value: T) => void): () => void;\n\ton(\n\t\tevent: Exclude<MotionValueEvent, \"change\">,\n\t\tlistener: () => void,\n\t): () => void;\n\t/** Stops the running animation (`animationCancel`), then `animationStart`. */\n\tstart(animation: MotionValueAnimation): void;\n\t/** Stops the running animation, if any, with `animationCancel`. */\n\tstop(): void;\n\tisAnimating(): boolean;\n\tdestroy(): void;\n}\n\nconst STALE_AFTER = 30;\nconst values = new WeakSet<object>();\n\nexport function isMotionValue(value: unknown): value is MotionValue<unknown> {\n\treturn typeof value === \"object\" && value !== null && values.has(value);\n}\n\nexport function motionValue<T>(initial: T): MotionValue<T> {\n\tlet current = initial;\n\tlet updatedAt = now();\n\tlet last: T | undefined;\n\tlet previous: T | undefined;\n\tlet previousAt = updatedAt;\n\tlet animation: MotionValueAnimation | undefined;\n\tconst listeners = new Map<MotionValueEvent, Set<(value?: T) => void>>();\n\n\tfunction emit(event: MotionValueEvent): void {\n\t\tfor (const listener of listeners.get(event) ?? []) listener(current);\n\t}\n\n\tfunction stop(): void {\n\t\tconst running = animation;\n\t\tif (!running) return;\n\t\t// Cleared first: the animation's own stop may call back into this value.\n\t\tanimation = undefined;\n\t\trunning.stop();\n\t\temit(\"animationCancel\");\n\t}\n\n\tconst value: MotionValue<T> = {\n\t\tget: () => current,\n\t\tset(next) {\n\t\t\tconst time = now();\n\t\t\t// Two sets in one frame keep the previous frame's sample, so velocity\n\t\t\t// spans real time instead of dividing by zero.\n\t\t\tif (time !== updatedAt) {\n\t\t\t\tprevious = current;\n\t\t\t\tpreviousAt = updatedAt;\n\t\t\t}\n\t\t\tlast = current;\n\t\t\tcurrent = next;\n\t\t\tupdatedAt = time;\n\t\t\temit(\"change\");\n\t\t},\n\t\tjump(next) {\n\t\t\tstop();\n\t\t\tlast = current;\n\t\t\tcurrent = next;\n\t\t\tprevious = undefined;\n\t\t\tupdatedAt = now();\n\t\t\tpreviousAt = updatedAt;\n\t\t\temit(\"change\");\n\t\t},\n\t\tgetVelocity() {\n\t\t\tconst elapsed = updatedAt - previousAt;\n\t\t\tif (\n\t\t\t\ttypeof current !== \"number\" ||\n\t\t\t\ttypeof previous !== \"number\" ||\n\t\t\t\telapsed <= 0 ||\n\t\t\t\tnow() - updatedAt > STALE_AFTER\n\t\t\t) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\treturn ((current - previous) / elapsed) * 1000;\n\t\t},\n\t\tgetPrevious: () => last,\n\t\t// One implementation behind both overloads: a lifecycle listener ignores the argument.\n\t\ton: ((event: MotionValueEvent, listener: (value?: T) => void) => {\n\t\t\tconst set = listeners.get(event) ?? new Set();\n\t\t\tlisteners.set(event, set);\n\t\t\tset.add(listener);\n\t\t\treturn () => {\n\t\t\t\tset.delete(listener);\n\t\t\t};\n\t\t}) as MotionValue<T>[\"on\"],\n\t\tstart(next) {\n\t\t\tstop();\n\t\t\tanimation = next;\n\t\t\temit(\"animationStart\");\n\t\t\tnext.finished?.then(() => {\n\t\t\t\tif (animation !== next) return;\n\t\t\t\tanimation = undefined;\n\t\t\t\temit(\"animationComplete\");\n\t\t\t});\n\t\t},\n\t\tstop,\n\t\tisAnimating: () => animation !== undefined,\n\t\tdestroy() {\n\t\t\tstop();\n\t\t\tlisteners.clear();\n\t\t},\n\t};\n\tvalues.add(value);\n\treturn value;\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,WAAW;AAoCpB,MAAM,cAAc;AACpB,MAAM,SAAS,oBAAI,QAAgB;AAE5B,SAAS,cAAc,OAA+C;AAC5E,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,OAAO,IAAI,KAAK;AACvE;AAEO,SAAS,YAAe,SAA4B;AAC1D,MAAI,UAAU;AACd,MAAI,YAAY,IAAI;AACpB,MAAI;AACJ,MAAI;AACJ,MAAI,aAAa;AACjB,MAAI;AACJ,QAAM,YAAY,oBAAI,IAAgD;AAEtE,WAAS,KAAK,OAA+B;AAC5C,eAAW,YAAY,UAAU,IAAI,KAAK,KAAK,CAAC,EAAG,UAAS,OAAO;AAAA,EACpE;AAEA,WAAS,OAAa;AACrB,UAAM,UAAU;AAChB,QAAI,CAAC,QAAS;AAEd,gBAAY;AACZ,YAAQ,KAAK;AACb,SAAK,iBAAiB;AAAA,EACvB;AAEA,QAAM,QAAwB;AAAA,IAC7B,KAAK,MAAM;AAAA,IACX,IAAI,MAAM;AACT,YAAM,OAAO,IAAI;AAGjB,UAAI,SAAS,WAAW;AACvB,mBAAW;AACX,qBAAa;AAAA,MACd;AACA,aAAO;AACP,gBAAU;AACV,kBAAY;AACZ,WAAK,QAAQ;AAAA,IACd;AAAA,IACA,KAAK,MAAM;AACV,WAAK;AACL,aAAO;AACP,gBAAU;AACV,iBAAW;AACX,kBAAY,IAAI;AAChB,mBAAa;AACb,WAAK,QAAQ;AAAA,IACd;AAAA,IACA,cAAc;AACb,YAAM,UAAU,YAAY;AAC5B,UACC,OAAO,YAAY,YACnB,OAAO,aAAa,YACpB,WAAW,KACX,IAAI,IAAI,YAAY,aACnB;AACD,eAAO;AAAA,MACR;AACA,cAAS,UAAU,YAAY,UAAW;AAAA,IAC3C;AAAA,IACA,aAAa,MAAM;AAAA;AAAA,IAEnB,KAAK,CAAC,OAAyB,aAAkC;AAChE,YAAM,MAAM,UAAU,IAAI,KAAK,KAAK,oBAAI,IAAI;AAC5C,gBAAU,IAAI,OAAO,GAAG;AACxB,UAAI,IAAI,QAAQ;AAChB,aAAO,MAAM;AACZ,YAAI,OAAO,QAAQ;AAAA,MACpB;AAAA,IACD;AAAA,IACA,MAAM,MAAM;AACX,WAAK;AACL,kBAAY;AACZ,WAAK,gBAAgB;AACrB,WAAK,UAAU,KAAK,MAAM;AACzB,YAAI,cAAc,KAAM;AACxB,oBAAY;AACZ,aAAK,mBAAmB;AAAA,MACzB,CAAC;AAAA,IACF;AAAA,IACA;AAAA,IACA,aAAa,MAAM,cAAc;AAAA,IACjC,UAAU;AACT,WAAK;AACL,gBAAU,MAAM;AAAA,IACjB;AAAA,EACD;AACA,SAAO,IAAI,KAAK;AAChB,SAAO;AACR;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type InterpolateOptions } from "../interpolate/interpolate.js";
|
|
2
|
+
import { type MotionValue } from "./motion-value.js";
|
|
3
|
+
type Values<V extends readonly unknown[]> = {
|
|
4
|
+
readonly [K in keyof V]: MotionValue<V[K]>;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* A value derived from others, recomputed on each of their changes:
|
|
8
|
+
* `transformValue(x, [0, 100], [1, 0])` maps through a range,
|
|
9
|
+
* `transformValue([x, y], (x, y) => Math.hypot(x, y))` combines. Its
|
|
10
|
+
* `destroy()` lets go of the sources.
|
|
11
|
+
*/
|
|
12
|
+
export declare function transformValue<T>(source: MotionValue<number>, input: readonly number[], output: readonly T[], options?: InterpolateOptions<T>): MotionValue<T>;
|
|
13
|
+
export declare function transformValue<S, T>(source: MotionValue<S>, compute: (value: S) => T): MotionValue<T>;
|
|
14
|
+
export declare function transformValue<V extends readonly unknown[], T>(sources: Values<V>, compute: (...values: V) => T): MotionValue<T>;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=transform.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../src/value/transform.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,kBAAkB,EAEvB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,KAAK,WAAW,EAAe,MAAM,yBAAyB,CAAC;AAExE,KAAK,MAAM,CAAC,CAAC,SAAS,SAAS,OAAO,EAAE,IAAI;IAC3C,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAC1C,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC/B,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,EAC3B,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,MAAM,EAAE,SAAS,CAAC,EAAE,EACpB,OAAO,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAC7B,WAAW,CAAC,CAAC,CAAC,CAAC;AAClB,wBAAgB,cAAc,CAAC,CAAC,EAAE,CAAC,EAClC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,EACtB,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GACtB,WAAW,CAAC,CAAC,CAAC,CAAC;AAClB,wBAAgB,cAAc,CAAC,CAAC,SAAS,SAAS,OAAO,EAAE,EAAE,CAAC,EAC7D,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAClB,OAAO,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,GAC1B,WAAW,CAAC,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
interpolate
|
|
3
|
+
} from "../interpolate/interpolate.js";
|
|
4
|
+
import { motionValue } from "./motion-value.js";
|
|
5
|
+
function transformValue(sources, inputOrCompute, output, options) {
|
|
6
|
+
const list = Array.isArray(sources) ? sources : [sources];
|
|
7
|
+
const compute = typeof inputOrCompute === "function" ? inputOrCompute : mapThrough(interpolate(inputOrCompute, output ?? [], options));
|
|
8
|
+
function read() {
|
|
9
|
+
return compute(...list.map((source) => source.get()));
|
|
10
|
+
}
|
|
11
|
+
const derived = motionValue(read());
|
|
12
|
+
const unsubscribes = list.map(
|
|
13
|
+
(source) => source.on("change", () => derived.set(read()))
|
|
14
|
+
);
|
|
15
|
+
const destroy = derived.destroy;
|
|
16
|
+
derived.destroy = () => {
|
|
17
|
+
for (const unsubscribe of unsubscribes) unsubscribe();
|
|
18
|
+
destroy();
|
|
19
|
+
};
|
|
20
|
+
return derived;
|
|
21
|
+
}
|
|
22
|
+
function mapThrough(map) {
|
|
23
|
+
return (value) => map(value);
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
transformValue
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=transform.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/value/transform.ts"],
|
|
4
|
+
"sourcesContent": ["import {\n\ttype InterpolateOptions,\n\tinterpolate,\n} from \"#/interpolate/interpolate.ts\";\nimport { type MotionValue, motionValue } from \"#/value/motion-value.ts\";\n\ntype Values<V extends readonly unknown[]> = {\n\treadonly [K in keyof V]: MotionValue<V[K]>;\n};\n\n/**\n * A value derived from others, recomputed on each of their changes:\n * `transformValue(x, [0, 100], [1, 0])` maps through a range,\n * `transformValue([x, y], (x, y) => Math.hypot(x, y))` combines. Its\n * `destroy()` lets go of the sources.\n */\nexport function transformValue<T>(\n\tsource: MotionValue<number>,\n\tinput: readonly number[],\n\toutput: readonly T[],\n\toptions?: InterpolateOptions<T>,\n): MotionValue<T>;\nexport function transformValue<S, T>(\n\tsource: MotionValue<S>,\n\tcompute: (value: S) => T,\n): MotionValue<T>;\nexport function transformValue<V extends readonly unknown[], T>(\n\tsources: Values<V>,\n\tcompute: (...values: V) => T,\n): MotionValue<T>;\nexport function transformValue<T>(\n\tsources: MotionValue<unknown> | readonly MotionValue<unknown>[],\n\tinputOrCompute: readonly number[] | ((...values: never[]) => T),\n\toutput?: readonly T[],\n\toptions?: InterpolateOptions<T>,\n): MotionValue<T> {\n\tconst list: readonly MotionValue<unknown>[] = Array.isArray(sources)\n\t\t? sources\n\t\t: [sources as MotionValue<unknown>];\n\tconst compute =\n\t\ttypeof inputOrCompute === \"function\"\n\t\t\t? (inputOrCompute as (...values: unknown[]) => T)\n\t\t\t: mapThrough(interpolate(inputOrCompute, output ?? [], options));\n\tfunction read(): T {\n\t\treturn compute(...list.map((source) => source.get()));\n\t}\n\tconst derived = motionValue(read());\n\tconst unsubscribes = list.map((source) =>\n\t\tsource.on(\"change\", () => derived.set(read())),\n\t);\n\tconst destroy = derived.destroy;\n\tderived.destroy = () => {\n\t\tfor (const unsubscribe of unsubscribes) unsubscribe();\n\t\tdestroy();\n\t};\n\treturn derived;\n}\n\nfunction mapThrough<T>(map: (value: number) => T): (...values: unknown[]) => T {\n\treturn (value) => map(value as number);\n}\n"],
|
|
5
|
+
"mappings": "AAAA;AAAA,EAEC;AAAA,OACM;AACP,SAA2B,mBAAmB;AA0BvC,SAAS,eACf,SACA,gBACA,QACA,SACiB;AACjB,QAAM,OAAwC,MAAM,QAAQ,OAAO,IAChE,UACA,CAAC,OAA+B;AACnC,QAAM,UACL,OAAO,mBAAmB,aACtB,iBACD,WAAW,YAAY,gBAAgB,UAAU,CAAC,GAAG,OAAO,CAAC;AACjE,WAAS,OAAU;AAClB,WAAO,QAAQ,GAAG,KAAK,IAAI,CAAC,WAAW,OAAO,IAAI,CAAC,CAAC;AAAA,EACrD;AACA,QAAM,UAAU,YAAY,KAAK,CAAC;AAClC,QAAM,eAAe,KAAK;AAAA,IAAI,CAAC,WAC9B,OAAO,GAAG,UAAU,MAAM,QAAQ,IAAI,KAAK,CAAC,CAAC;AAAA,EAC9C;AACA,QAAM,UAAU,QAAQ;AACxB,UAAQ,UAAU,MAAM;AACvB,eAAW,eAAe,aAAc,aAAY;AACpD,YAAQ;AAAA,EACT;AACA,SAAO;AACR;AAEA,SAAS,WAAc,KAAwD;AAC9E,SAAO,CAAC,UAAU,IAAI,KAAe;AACtC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type InViewAmount = "some" | "all" | number;
|
|
2
|
+
export interface InViewOptions {
|
|
3
|
+
/** The scrolling ancestor to intersect with; the viewport by default. */
|
|
4
|
+
readonly root?: Element | Document;
|
|
5
|
+
/** Grows or shrinks the root's box, CSS margin syntax: "0px 0px -20% 0px". */
|
|
6
|
+
readonly margin?: string;
|
|
7
|
+
/** How much of the element must show: any pixel, all of it, or a fraction. */
|
|
8
|
+
readonly amount?: InViewAmount;
|
|
9
|
+
}
|
|
10
|
+
export type OnViewEnd = (entry: IntersectionObserverEntry) => void;
|
|
11
|
+
export type OnViewStart = (element: Element, entry: IntersectionObserverEntry) => undefined | OnViewEnd;
|
|
12
|
+
/**
|
|
13
|
+
* Calls `onStart` when an element comes into view. When it returns a
|
|
14
|
+
* function, that runs when the element leaves and `onStart` runs again on
|
|
15
|
+
* the next entry; otherwise the element fires once. Returns a function that
|
|
16
|
+
* stops observing.
|
|
17
|
+
*/
|
|
18
|
+
export declare function inView(target: Element | string | readonly Element[], onStart: OnViewStart, options?: InViewOptions): () => void;
|
|
19
|
+
//# sourceMappingURL=in-view.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"in-view.d.ts","sourceRoot":"","sources":["../../src/view/in-view.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;AAEnD,MAAM,WAAW,aAAa;IAC7B,yEAAyE;IACzE,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IACnC,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC;CAC/B;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,yBAAyB,KAAK,IAAI,CAAC;AACnE,MAAM,MAAM,WAAW,GAAG,CACzB,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,yBAAyB,KAC5B,SAAS,GAAG,SAAS,CAAC;AAO3B;;;;;GAKG;AACH,wBAAgB,MAAM,CACrB,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,OAAO,EAAE,EAC7C,OAAO,EAAE,WAAW,EACpB,OAAO,GAAE,aAAkB,GACzB,MAAM,IAAI,CAuCZ"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { resolveElements } from "./resolve-elements.js";
|
|
2
|
+
const THRESHOLDS = {
|
|
3
|
+
some: 0,
|
|
4
|
+
all: 1
|
|
5
|
+
};
|
|
6
|
+
function inView(target, onStart, options = {}) {
|
|
7
|
+
const elements = resolveElements(target);
|
|
8
|
+
if (elements.length === 0 || typeof IntersectionObserver === "undefined") {
|
|
9
|
+
return () => {
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
const amount = options.amount ?? "some";
|
|
13
|
+
const threshold = typeof amount === "number" ? amount : THRESHOLDS[amount];
|
|
14
|
+
const active = /* @__PURE__ */ new Map();
|
|
15
|
+
function onIntersect(entries) {
|
|
16
|
+
for (const entry of entries) {
|
|
17
|
+
const visible = entry.isIntersecting && entry.intersectionRatio + 1e-3 >= threshold;
|
|
18
|
+
const onEnd = active.get(entry.target);
|
|
19
|
+
if (visible && onEnd === void 0) {
|
|
20
|
+
const next = onStart(entry.target, entry);
|
|
21
|
+
if (typeof next === "function") {
|
|
22
|
+
active.set(entry.target, next);
|
|
23
|
+
} else {
|
|
24
|
+
observer.unobserve(entry.target);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (!visible && onEnd !== void 0) {
|
|
28
|
+
active.delete(entry.target);
|
|
29
|
+
onEnd(entry);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const observer = new IntersectionObserver(onIntersect, {
|
|
34
|
+
root: options.root,
|
|
35
|
+
rootMargin: options.margin,
|
|
36
|
+
threshold
|
|
37
|
+
});
|
|
38
|
+
for (const element of elements) {
|
|
39
|
+
observer.observe(element);
|
|
40
|
+
}
|
|
41
|
+
return () => observer.disconnect();
|
|
42
|
+
}
|
|
43
|
+
export {
|
|
44
|
+
inView
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=in-view.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/view/in-view.ts"],
|
|
4
|
+
"sourcesContent": ["import { resolveElements } from \"#/view/resolve-elements.ts\";\n\nexport type InViewAmount = \"some\" | \"all\" | number;\n\nexport interface InViewOptions {\n\t/** The scrolling ancestor to intersect with; the viewport by default. */\n\treadonly root?: Element | Document;\n\t/** Grows or shrinks the root's box, CSS margin syntax: \"0px 0px -20% 0px\". */\n\treadonly margin?: string;\n\t/** How much of the element must show: any pixel, all of it, or a fraction. */\n\treadonly amount?: InViewAmount;\n}\n\nexport type OnViewEnd = (entry: IntersectionObserverEntry) => void;\nexport type OnViewStart = (\n\telement: Element,\n\tentry: IntersectionObserverEntry,\n) => undefined | OnViewEnd;\n\nconst THRESHOLDS: Readonly<Record<\"some\" | \"all\", number>> = {\n\tsome: 0,\n\tall: 1,\n};\n\n/**\n * Calls `onStart` when an element comes into view. When it returns a\n * function, that runs when the element leaves and `onStart` runs again on\n * the next entry; otherwise the element fires once. Returns a function that\n * stops observing.\n */\nexport function inView(\n\ttarget: Element | string | readonly Element[],\n\tonStart: OnViewStart,\n\toptions: InViewOptions = {},\n): () => void {\n\tconst elements = resolveElements(target);\n\tif (elements.length === 0 || typeof IntersectionObserver === \"undefined\") {\n\t\treturn () => {};\n\t}\n\tconst amount = options.amount ?? \"some\";\n\tconst threshold = typeof amount === \"number\" ? amount : THRESHOLDS[amount];\n\tconst active = new Map<Element, OnViewEnd>();\n\n\tfunction onIntersect(entries: IntersectionObserverEntry[]) {\n\t\tfor (const entry of entries) {\n\t\t\t// Chromium reports a fully visible box as 0.99\u2026 on fractional layouts.\n\t\t\tconst visible =\n\t\t\t\tentry.isIntersecting && entry.intersectionRatio + 1e-3 >= threshold;\n\t\t\tconst onEnd = active.get(entry.target);\n\t\t\tif (visible && onEnd === undefined) {\n\t\t\t\tconst next = onStart(entry.target, entry);\n\t\t\t\tif (typeof next === \"function\") {\n\t\t\t\t\tactive.set(entry.target, next);\n\t\t\t\t} else {\n\t\t\t\t\tobserver.unobserve(entry.target);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!visible && onEnd !== undefined) {\n\t\t\t\tactive.delete(entry.target);\n\t\t\t\tonEnd(entry);\n\t\t\t}\n\t\t}\n\t}\n\n\tconst observer = new IntersectionObserver(onIntersect, {\n\t\troot: options.root,\n\t\trootMargin: options.margin,\n\t\tthreshold,\n\t});\n\tfor (const element of elements) {\n\t\tobserver.observe(element);\n\t}\n\treturn () => observer.disconnect();\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,uBAAuB;AAmBhC,MAAM,aAAuD;AAAA,EAC5D,MAAM;AAAA,EACN,KAAK;AACN;AAQO,SAAS,OACf,QACA,SACA,UAAyB,CAAC,GACb;AACb,QAAM,WAAW,gBAAgB,MAAM;AACvC,MAAI,SAAS,WAAW,KAAK,OAAO,yBAAyB,aAAa;AACzE,WAAO,MAAM;AAAA,IAAC;AAAA,EACf;AACA,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,YAAY,OAAO,WAAW,WAAW,SAAS,WAAW,MAAM;AACzE,QAAM,SAAS,oBAAI,IAAwB;AAE3C,WAAS,YAAY,SAAsC;AAC1D,eAAW,SAAS,SAAS;AAE5B,YAAM,UACL,MAAM,kBAAkB,MAAM,oBAAoB,QAAQ;AAC3D,YAAM,QAAQ,OAAO,IAAI,MAAM,MAAM;AACrC,UAAI,WAAW,UAAU,QAAW;AACnC,cAAM,OAAO,QAAQ,MAAM,QAAQ,KAAK;AACxC,YAAI,OAAO,SAAS,YAAY;AAC/B,iBAAO,IAAI,MAAM,QAAQ,IAAI;AAAA,QAC9B,OAAO;AACN,mBAAS,UAAU,MAAM,MAAM;AAAA,QAChC;AAAA,MACD;AACA,UAAI,CAAC,WAAW,UAAU,QAAW;AACpC,eAAO,OAAO,MAAM,MAAM;AAC1B,cAAM,KAAK;AAAA,MACZ;AAAA,IACD;AAAA,EACD;AAEA,QAAM,WAAW,IAAI,qBAAqB,aAAa;AAAA,IACtD,MAAM,QAAQ;AAAA,IACd,YAAY,QAAQ;AAAA,IACpB;AAAA,EACD,CAAC;AACD,aAAW,WAAW,UAAU;AAC/B,aAAS,QAAQ,OAAO;AAAA,EACzB;AACA,SAAO,MAAM,SAAS,WAAW;AAClC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface Size {
|
|
2
|
+
readonly width: number;
|
|
3
|
+
readonly height: number;
|
|
4
|
+
}
|
|
5
|
+
export type OnElementResize = (element: Element, size: Size) => void;
|
|
6
|
+
export type OnWindowResize = (size: Size) => void;
|
|
7
|
+
/**
|
|
8
|
+
* Calls back when an element's border box changes size, or when the window
|
|
9
|
+
* does. Returns a function that stops listening.
|
|
10
|
+
*/
|
|
11
|
+
export declare function resize(onResize: OnWindowResize): () => void;
|
|
12
|
+
export declare function resize(target: Element | string | readonly Element[], onResize: OnElementResize): () => void;
|
|
13
|
+
//# sourceMappingURL=resize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resize.d.ts","sourceRoot":"","sources":["../../src/view/resize.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,IAAI;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;AACrE,MAAM,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;AAyElD;;;GAGG;AACH,wBAAgB,MAAM,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,IAAI,CAAC;AAC7D,wBAAgB,MAAM,CACrB,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,OAAO,EAAE,EAC7C,QAAQ,EAAE,eAAe,GACvB,MAAM,IAAI,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { resolveElements } from "./resolve-elements.js";
|
|
2
|
+
let observer;
|
|
3
|
+
const elementListeners = /* @__PURE__ */ new Map();
|
|
4
|
+
const windowListeners = /* @__PURE__ */ new Set();
|
|
5
|
+
function boxSize(entry) {
|
|
6
|
+
const box = entry.borderBoxSize?.[0];
|
|
7
|
+
return box ? { width: box.inlineSize, height: box.blockSize } : { width: entry.contentRect.width, height: entry.contentRect.height };
|
|
8
|
+
}
|
|
9
|
+
function notifyElements(entries) {
|
|
10
|
+
for (const entry of entries) {
|
|
11
|
+
const size = boxSize(entry);
|
|
12
|
+
for (const listener of elementListeners.get(entry.target) ?? []) {
|
|
13
|
+
listener(entry.target, size);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function notifyWindow() {
|
|
18
|
+
const size = { width: window.innerWidth, height: window.innerHeight };
|
|
19
|
+
for (const listener of windowListeners) {
|
|
20
|
+
listener(size);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function resizeWindow(onResize) {
|
|
24
|
+
if (windowListeners.size === 0) {
|
|
25
|
+
window.addEventListener("resize", notifyWindow);
|
|
26
|
+
}
|
|
27
|
+
windowListeners.add(onResize);
|
|
28
|
+
return () => {
|
|
29
|
+
windowListeners.delete(onResize);
|
|
30
|
+
if (windowListeners.size === 0) {
|
|
31
|
+
window.removeEventListener("resize", notifyWindow);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function resizeElements(target, onResize) {
|
|
36
|
+
if (typeof ResizeObserver === "undefined") {
|
|
37
|
+
return () => {
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
observer ??= new ResizeObserver(notifyElements);
|
|
41
|
+
const elements = resolveElements(target);
|
|
42
|
+
for (const element of elements) {
|
|
43
|
+
const listeners = elementListeners.get(element) ?? /* @__PURE__ */ new Set();
|
|
44
|
+
if (listeners.size === 0) {
|
|
45
|
+
elementListeners.set(element, listeners);
|
|
46
|
+
observer.observe(element);
|
|
47
|
+
}
|
|
48
|
+
listeners.add(onResize);
|
|
49
|
+
}
|
|
50
|
+
return () => {
|
|
51
|
+
for (const element of elements) {
|
|
52
|
+
const listeners = elementListeners.get(element);
|
|
53
|
+
listeners?.delete(onResize);
|
|
54
|
+
if (listeners?.size === 0) {
|
|
55
|
+
elementListeners.delete(element);
|
|
56
|
+
observer?.unobserve(element);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function resize(targetOrListener, onResize) {
|
|
62
|
+
if (typeof targetOrListener === "function") {
|
|
63
|
+
return typeof window === "undefined" ? () => {
|
|
64
|
+
} : resizeWindow(targetOrListener);
|
|
65
|
+
}
|
|
66
|
+
return onResize ? resizeElements(targetOrListener, onResize) : () => {
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export {
|
|
70
|
+
resize
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=resize.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/view/resize.ts"],
|
|
4
|
+
"sourcesContent": ["import { resolveElements } from \"#/view/resolve-elements.ts\";\n\nexport interface Size {\n\treadonly width: number;\n\treadonly height: number;\n}\n\nexport type OnElementResize = (element: Element, size: Size) => void;\nexport type OnWindowResize = (size: Size) => void;\n\n// One observer for every element on the page: a ResizeObserver per call\n// would multiply the browser's work by the number of animated elements.\nlet observer: ResizeObserver | undefined;\nconst elementListeners = new Map<Element, Set<OnElementResize>>();\nconst windowListeners = new Set<OnWindowResize>();\n\nfunction boxSize(entry: ResizeObserverEntry): Size {\n\tconst box = entry.borderBoxSize?.[0];\n\treturn box\n\t\t? { width: box.inlineSize, height: box.blockSize }\n\t\t: { width: entry.contentRect.width, height: entry.contentRect.height };\n}\n\nfunction notifyElements(entries: ResizeObserverEntry[]) {\n\tfor (const entry of entries) {\n\t\tconst size = boxSize(entry);\n\t\tfor (const listener of elementListeners.get(entry.target) ?? []) {\n\t\t\tlistener(entry.target, size);\n\t\t}\n\t}\n}\n\nfunction notifyWindow() {\n\tconst size = { width: window.innerWidth, height: window.innerHeight };\n\tfor (const listener of windowListeners) {\n\t\tlistener(size);\n\t}\n}\n\nfunction resizeWindow(onResize: OnWindowResize): () => void {\n\tif (windowListeners.size === 0) {\n\t\twindow.addEventListener(\"resize\", notifyWindow);\n\t}\n\twindowListeners.add(onResize);\n\treturn () => {\n\t\twindowListeners.delete(onResize);\n\t\tif (windowListeners.size === 0) {\n\t\t\twindow.removeEventListener(\"resize\", notifyWindow);\n\t\t}\n\t};\n}\n\nfunction resizeElements(\n\ttarget: Element | string | readonly Element[],\n\tonResize: OnElementResize,\n): () => void {\n\tif (typeof ResizeObserver === \"undefined\") {\n\t\treturn () => {};\n\t}\n\tobserver ??= new ResizeObserver(notifyElements);\n\tconst elements = resolveElements(target);\n\tfor (const element of elements) {\n\t\tconst listeners = elementListeners.get(element) ?? new Set();\n\t\tif (listeners.size === 0) {\n\t\t\telementListeners.set(element, listeners);\n\t\t\tobserver.observe(element);\n\t\t}\n\t\tlisteners.add(onResize);\n\t}\n\treturn () => {\n\t\tfor (const element of elements) {\n\t\t\tconst listeners = elementListeners.get(element);\n\t\t\tlisteners?.delete(onResize);\n\t\t\tif (listeners?.size === 0) {\n\t\t\t\telementListeners.delete(element);\n\t\t\t\tobserver?.unobserve(element);\n\t\t\t}\n\t\t}\n\t};\n}\n\n/**\n * Calls back when an element's border box changes size, or when the window\n * does. Returns a function that stops listening.\n */\nexport function resize(onResize: OnWindowResize): () => void;\nexport function resize(\n\ttarget: Element | string | readonly Element[],\n\tonResize: OnElementResize,\n): () => void;\nexport function resize(\n\ttargetOrListener: Element | string | readonly Element[] | OnWindowResize,\n\tonResize?: OnElementResize,\n): () => void {\n\tif (typeof targetOrListener === \"function\") {\n\t\treturn typeof window === \"undefined\"\n\t\t\t? () => {}\n\t\t\t: resizeWindow(targetOrListener);\n\t}\n\treturn onResize ? resizeElements(targetOrListener, onResize) : () => {};\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,uBAAuB;AAYhC,IAAI;AACJ,MAAM,mBAAmB,oBAAI,IAAmC;AAChE,MAAM,kBAAkB,oBAAI,IAAoB;AAEhD,SAAS,QAAQ,OAAkC;AAClD,QAAM,MAAM,MAAM,gBAAgB,CAAC;AACnC,SAAO,MACJ,EAAE,OAAO,IAAI,YAAY,QAAQ,IAAI,UAAU,IAC/C,EAAE,OAAO,MAAM,YAAY,OAAO,QAAQ,MAAM,YAAY,OAAO;AACvE;AAEA,SAAS,eAAe,SAAgC;AACvD,aAAW,SAAS,SAAS;AAC5B,UAAM,OAAO,QAAQ,KAAK;AAC1B,eAAW,YAAY,iBAAiB,IAAI,MAAM,MAAM,KAAK,CAAC,GAAG;AAChE,eAAS,MAAM,QAAQ,IAAI;AAAA,IAC5B;AAAA,EACD;AACD;AAEA,SAAS,eAAe;AACvB,QAAM,OAAO,EAAE,OAAO,OAAO,YAAY,QAAQ,OAAO,YAAY;AACpE,aAAW,YAAY,iBAAiB;AACvC,aAAS,IAAI;AAAA,EACd;AACD;AAEA,SAAS,aAAa,UAAsC;AAC3D,MAAI,gBAAgB,SAAS,GAAG;AAC/B,WAAO,iBAAiB,UAAU,YAAY;AAAA,EAC/C;AACA,kBAAgB,IAAI,QAAQ;AAC5B,SAAO,MAAM;AACZ,oBAAgB,OAAO,QAAQ;AAC/B,QAAI,gBAAgB,SAAS,GAAG;AAC/B,aAAO,oBAAoB,UAAU,YAAY;AAAA,IAClD;AAAA,EACD;AACD;AAEA,SAAS,eACR,QACA,UACa;AACb,MAAI,OAAO,mBAAmB,aAAa;AAC1C,WAAO,MAAM;AAAA,IAAC;AAAA,EACf;AACA,eAAa,IAAI,eAAe,cAAc;AAC9C,QAAM,WAAW,gBAAgB,MAAM;AACvC,aAAW,WAAW,UAAU;AAC/B,UAAM,YAAY,iBAAiB,IAAI,OAAO,KAAK,oBAAI,IAAI;AAC3D,QAAI,UAAU,SAAS,GAAG;AACzB,uBAAiB,IAAI,SAAS,SAAS;AACvC,eAAS,QAAQ,OAAO;AAAA,IACzB;AACA,cAAU,IAAI,QAAQ;AAAA,EACvB;AACA,SAAO,MAAM;AACZ,eAAW,WAAW,UAAU;AAC/B,YAAM,YAAY,iBAAiB,IAAI,OAAO;AAC9C,iBAAW,OAAO,QAAQ;AAC1B,UAAI,WAAW,SAAS,GAAG;AAC1B,yBAAiB,OAAO,OAAO;AAC/B,kBAAU,UAAU,OAAO;AAAA,MAC5B;AAAA,IACD;AAAA,EACD;AACD;AAWO,SAAS,OACf,kBACA,UACa;AACb,MAAI,OAAO,qBAAqB,YAAY;AAC3C,WAAO,OAAO,WAAW,cACtB,MAAM;AAAA,IAAC,IACP,aAAa,gBAAgB;AAAA,EACjC;AACA,SAAO,WAAW,eAAe,kBAAkB,QAAQ,IAAI,MAAM;AAAA,EAAC;AACvE;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-elements.d.ts","sourceRoot":"","sources":["../../src/view/resolve-elements.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,wBAAgB,eAAe,CAC9B,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,OAAO,EAAE,GAC3C,SAAS,OAAO,EAAE,CAOpB"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
function resolveElements(target) {
|
|
2
|
+
if (typeof target === "string") {
|
|
3
|
+
return typeof document === "undefined" ? [] : Array.from(document.querySelectorAll(target));
|
|
4
|
+
}
|
|
5
|
+
return "length" in target ? target : [target];
|
|
6
|
+
}
|
|
7
|
+
export {
|
|
8
|
+
resolveElements
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=resolve-elements.js.map
|