@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
package/README.md
CHANGED
|
@@ -1,3 +1,106 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @voila.dev/motion
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Animation with no dependency. Springs on elements become a CSS `linear()`
|
|
4
|
+
easing that the browser plays through the Web Animations API, off the main
|
|
5
|
+
thread. Everything the browser can't animate runs on one shared frame loop.
|
|
6
|
+
The API follows [Motion](https://motion.dev), and each module is short enough
|
|
7
|
+
to read in one sitting.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { animate, stagger } from "@voila.dev/motion";
|
|
11
|
+
|
|
12
|
+
animate(element, { x: 120, opacity: 1 }, { duration: 0.4, bounce: 0.2 });
|
|
13
|
+
animate("li", { y: [20, 0], opacity: [0, 1] }, { delay: stagger(0.04) });
|
|
14
|
+
await animate(card, { scale: 1.05 });
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { Presence, useAnimate, useSpring, useTransform } from "@voila.dev/motion/react";
|
|
19
|
+
|
|
20
|
+
const [scope, animate] = useAnimate();
|
|
21
|
+
const x = useSpring(target, { duration: 0.3 });
|
|
22
|
+
const opacity = useTransform(x, [0, 100], [1, 0]);
|
|
23
|
+
|
|
24
|
+
<Presence>{open && <Dialog key="d" exit={(element) => animate(element, { opacity: 0 })} />}</Presence>;
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Entries
|
|
28
|
+
|
|
29
|
+
| Import | What it brings | Gzip |
|
|
30
|
+
| --- | --- | --- |
|
|
31
|
+
| `@voila.dev/motion/dom` | `animate` for elements only, every property played by the browser, plus `spring` and `stagger` | ≤ 5 KB |
|
|
32
|
+
| `@voila.dev/motion` | The full `animate` (elements, motion values, plain values, objects, SVG morphing, sequences), interpolation, motion values, the frame loop, `inView`, `scroll`, `resize`, reduced motion | about 11 KB |
|
|
33
|
+
| `@voila.dev/motion/react` | `useAnimate`, `useMotionValue`, `useSpring`, `useTransform`, `useReducedMotion`, `Presence`, `usePresence` | about 12.5 KB with the core |
|
|
34
|
+
|
|
35
|
+
React 19 is an optional peer, used only by `/react`. The core never imports it.
|
|
36
|
+
|
|
37
|
+
## The engine
|
|
38
|
+
|
|
39
|
+
- **Elements.** Each property is one browser animation. Two numeric keyframes
|
|
40
|
+
spring by default: the spring is sampled into a `linear()` easing, so the
|
|
41
|
+
compositor plays the exact curve. Without `linear()` support, a numeric
|
|
42
|
+
spring is sampled into keyframes instead. A tween uses its easing, written as CSS
|
|
43
|
+
when CSS has it.
|
|
44
|
+
- **Independent transforms.** `x`, `y`, `z`, `scale`, `scaleX`, `scaleY` and
|
|
45
|
+
`rotate` each animate the `translate`, `scale` or `rotate` property alone,
|
|
46
|
+
with `composite: "add"`. They run on the compositor, add up, and stop
|
|
47
|
+
independently. When they finish, their values are written into the
|
|
48
|
+
element's inline `translate`, `scale` and `rotate`: those three styles
|
|
49
|
+
belong to motion on an element it animates.
|
|
50
|
+
- **Interruption.** A new animation on a property starts from what is
|
|
51
|
+
painted, at the speed the last one had. For numbers, motion keeps the
|
|
52
|
+
generator behind each browser animation and reads both from it; for
|
|
53
|
+
colours and other strings, it asks the browser what it painted.
|
|
54
|
+
- **The frame loop.** Motion values, plain values, objects, SVG attributes
|
|
55
|
+
(`d` morphs between any two shapes) and CSS variables run on one
|
|
56
|
+
`requestAnimationFrame` loop, in `read → update → render` phases.
|
|
57
|
+
- **Controls.** Every `animate` returns the same awaitable controls: `time`,
|
|
58
|
+
`speed`, `duration`, `play`, `pause`, `stop`, `cancel`, `complete`,
|
|
59
|
+
`finished`.
|
|
60
|
+
|
|
61
|
+
## Modules
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
src/
|
|
65
|
+
easing/ cubicBezier, steps, the named easings, a spring as linear()
|
|
66
|
+
generators/ spring (closed form), tween, inertia: (t) => { value, velocity, done }
|
|
67
|
+
frame/ the shared loop and its injectable clock
|
|
68
|
+
value/ motionValue, transformValue
|
|
69
|
+
interpolate/ numbers, colours (oklab, or color-mix for var()), strings, arrays,
|
|
70
|
+
objects, path morphing, mix, interpolate
|
|
71
|
+
animate/ routing, the WAAPI driver, the JS driver, controls
|
|
72
|
+
sequence/ timelines with `at`
|
|
73
|
+
stagger/ stagger(gap, { from, ease })
|
|
74
|
+
view/ inView, scroll (ScrollTimeline when it can), resize
|
|
75
|
+
accessibility/ the reduced-motion policy
|
|
76
|
+
react/ the hooks and Presence
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Reduced motion
|
|
80
|
+
|
|
81
|
+
Under `prefers-reduced-motion: reduce`, movement jumps to its end. Opacity
|
|
82
|
+
and colours still animate as a short tween with no bounce. Values and objects
|
|
83
|
+
jump. `setReducedMotion("user" | "always" | "never")` sets the policy for the
|
|
84
|
+
whole page (`"user"` by default), and `{ reducedMotion }` overrides it for
|
|
85
|
+
one animation. `useReducedMotion()` follows both.
|
|
86
|
+
|
|
87
|
+
## Server rendering
|
|
88
|
+
|
|
89
|
+
Nothing touches `window` or `document` when a module loads, and the hooks
|
|
90
|
+
render on the server. Animations start in effects.
|
|
91
|
+
|
|
92
|
+
## Not in 1.0
|
|
93
|
+
|
|
94
|
+
- **Layout animations (FLIP, shared layout).** They are a module the size of
|
|
95
|
+
this one. Worth adding once a kit component needs them, like an accordion
|
|
96
|
+
or a tabs indicator.
|
|
97
|
+
- **Gestures (drag, hover, press).** They belong to the components that use
|
|
98
|
+
them.
|
|
99
|
+
- **A `<motion.div>` component.** It is Motion's most popular API, but it
|
|
100
|
+
doubles the surface. `useAnimate` and `Presence` cover the same needs with
|
|
101
|
+
fewer concepts.
|
|
102
|
+
|
|
103
|
+
## Size budget
|
|
104
|
+
|
|
105
|
+
`bun run check-bundle` builds each entry in `scripts/bundle-entries` and fails
|
|
106
|
+
past its gzip budget, or when an entry outside the React layer imports React.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "user" follows `prefers-reduced-motion`; "always" and "never" override it
|
|
3
|
+
* for the whole page, for an app with its own setting.
|
|
4
|
+
*/
|
|
5
|
+
export type ReducedMotionPolicy = "user" | "always" | "never";
|
|
6
|
+
export declare function prefersReducedMotion(): boolean;
|
|
7
|
+
export declare function reducedMotionPolicy(): ReducedMotionPolicy;
|
|
8
|
+
/** `true`/`false` force it for one animation; a policy overrides the global one. */
|
|
9
|
+
export declare function shouldReduceMotion(override?: ReducedMotionPolicy | boolean): boolean;
|
|
10
|
+
export declare function setReducedMotion(next: ReducedMotionPolicy): void;
|
|
11
|
+
/** Fires with the effective answer when the user's setting or the policy changes it. */
|
|
12
|
+
export declare function onReducedMotionChange(listener: (reduced: boolean) => void): () => void;
|
|
13
|
+
//# sourceMappingURL=reduced-motion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reduced-motion.d.ts","sourceRoot":"","sources":["../../src/accessibility/reduced-motion.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;AAgB9D,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAED,wBAAgB,mBAAmB,IAAI,mBAAmB,CAEzD;AAQD,oFAAoF;AACpF,wBAAgB,kBAAkB,CACjC,QAAQ,CAAC,EAAE,mBAAmB,GAAG,OAAO,GACtC,OAAO,CAGT;AAYD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,mBAAmB,GAAG,IAAI,CAIhE;AAED,wFAAwF;AACxF,wBAAgB,qBAAqB,CACpC,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAClC,MAAM,IAAI,CAaZ"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const QUERY = "(prefers-reduced-motion: reduce)";
|
|
2
|
+
let policy = "user";
|
|
3
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
4
|
+
let unwatch;
|
|
5
|
+
function mediaQuery() {
|
|
6
|
+
return typeof window === "undefined" || typeof window.matchMedia !== "function" ? void 0 : window.matchMedia(QUERY);
|
|
7
|
+
}
|
|
8
|
+
function prefersReducedMotion() {
|
|
9
|
+
return mediaQuery()?.matches ?? false;
|
|
10
|
+
}
|
|
11
|
+
function reducedMotionPolicy() {
|
|
12
|
+
return policy;
|
|
13
|
+
}
|
|
14
|
+
const DECIDE = {
|
|
15
|
+
user: prefersReducedMotion,
|
|
16
|
+
always: () => true,
|
|
17
|
+
never: () => false
|
|
18
|
+
};
|
|
19
|
+
function shouldReduceMotion(override) {
|
|
20
|
+
if (typeof override === "boolean") return override;
|
|
21
|
+
return DECIDE[override ?? policy]();
|
|
22
|
+
}
|
|
23
|
+
function notify() {
|
|
24
|
+
const reduced = shouldReduceMotion();
|
|
25
|
+
for (const listener of listeners) listener(reduced);
|
|
26
|
+
}
|
|
27
|
+
function onMediaChange() {
|
|
28
|
+
if (policy === "user") notify();
|
|
29
|
+
}
|
|
30
|
+
function setReducedMotion(next) {
|
|
31
|
+
const before = shouldReduceMotion();
|
|
32
|
+
policy = next;
|
|
33
|
+
if (shouldReduceMotion() !== before) notify();
|
|
34
|
+
}
|
|
35
|
+
function onReducedMotionChange(listener) {
|
|
36
|
+
listeners.add(listener);
|
|
37
|
+
if (!unwatch) {
|
|
38
|
+
const query = mediaQuery();
|
|
39
|
+
query?.addEventListener("change", onMediaChange);
|
|
40
|
+
unwatch = () => query?.removeEventListener("change", onMediaChange);
|
|
41
|
+
}
|
|
42
|
+
return () => {
|
|
43
|
+
listeners.delete(listener);
|
|
44
|
+
if (listeners.size > 0) return;
|
|
45
|
+
unwatch?.();
|
|
46
|
+
unwatch = void 0;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export {
|
|
50
|
+
onReducedMotionChange,
|
|
51
|
+
prefersReducedMotion,
|
|
52
|
+
reducedMotionPolicy,
|
|
53
|
+
setReducedMotion,
|
|
54
|
+
shouldReduceMotion
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=reduced-motion.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/accessibility/reduced-motion.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * \"user\" follows `prefers-reduced-motion`; \"always\" and \"never\" override it\n * for the whole page, for an app with its own setting.\n */\nexport type ReducedMotionPolicy = \"user\" | \"always\" | \"never\";\n\nconst QUERY = \"(prefers-reduced-motion: reduce)\";\n\nlet policy: ReducedMotionPolicy = \"user\";\nconst listeners = new Set<(reduced: boolean) => void>();\nlet unwatch: (() => void) | undefined;\n\n/** Read on every call, never at import: the server has no `matchMedia`. */\nfunction mediaQuery(): MediaQueryList | undefined {\n\treturn typeof window === \"undefined\" ||\n\t\ttypeof window.matchMedia !== \"function\"\n\t\t? undefined\n\t\t: window.matchMedia(QUERY);\n}\n\nexport function prefersReducedMotion(): boolean {\n\treturn mediaQuery()?.matches ?? false;\n}\n\nexport function reducedMotionPolicy(): ReducedMotionPolicy {\n\treturn policy;\n}\n\nconst DECIDE: Record<ReducedMotionPolicy, () => boolean> = {\n\tuser: prefersReducedMotion,\n\talways: () => true,\n\tnever: () => false,\n};\n\n/** `true`/`false` force it for one animation; a policy overrides the global one. */\nexport function shouldReduceMotion(\n\toverride?: ReducedMotionPolicy | boolean,\n): boolean {\n\tif (typeof override === \"boolean\") return override;\n\treturn DECIDE[override ?? policy]();\n}\n\nfunction notify(): void {\n\tconst reduced = shouldReduceMotion();\n\tfor (const listener of listeners) listener(reduced);\n}\n\n/** Under \"always\" or \"never\" the user's setting changes nothing. */\nfunction onMediaChange(): void {\n\tif (policy === \"user\") notify();\n}\n\nexport function setReducedMotion(next: ReducedMotionPolicy): void {\n\tconst before = shouldReduceMotion();\n\tpolicy = next;\n\tif (shouldReduceMotion() !== before) notify();\n}\n\n/** Fires with the effective answer when the user's setting or the policy changes it. */\nexport function onReducedMotionChange(\n\tlistener: (reduced: boolean) => void,\n): () => void {\n\tlisteners.add(listener);\n\tif (!unwatch) {\n\t\tconst query = mediaQuery();\n\t\tquery?.addEventListener(\"change\", onMediaChange);\n\t\tunwatch = () => query?.removeEventListener(\"change\", onMediaChange);\n\t}\n\treturn () => {\n\t\tlisteners.delete(listener);\n\t\tif (listeners.size > 0) return;\n\t\tunwatch?.();\n\t\tunwatch = undefined;\n\t};\n}\n"],
|
|
5
|
+
"mappings": "AAMA,MAAM,QAAQ;AAEd,IAAI,SAA8B;AAClC,MAAM,YAAY,oBAAI,IAAgC;AACtD,IAAI;AAGJ,SAAS,aAAyC;AACjD,SAAO,OAAO,WAAW,eACxB,OAAO,OAAO,eAAe,aAC3B,SACA,OAAO,WAAW,KAAK;AAC3B;AAEO,SAAS,uBAAgC;AAC/C,SAAO,WAAW,GAAG,WAAW;AACjC;AAEO,SAAS,sBAA2C;AAC1D,SAAO;AACR;AAEA,MAAM,SAAqD;AAAA,EAC1D,MAAM;AAAA,EACN,QAAQ,MAAM;AAAA,EACd,OAAO,MAAM;AACd;AAGO,SAAS,mBACf,UACU;AACV,MAAI,OAAO,aAAa,UAAW,QAAO;AAC1C,SAAO,OAAO,YAAY,MAAM,EAAE;AACnC;AAEA,SAAS,SAAe;AACvB,QAAM,UAAU,mBAAmB;AACnC,aAAW,YAAY,UAAW,UAAS,OAAO;AACnD;AAGA,SAAS,gBAAsB;AAC9B,MAAI,WAAW,OAAQ,QAAO;AAC/B;AAEO,SAAS,iBAAiB,MAAiC;AACjE,QAAM,SAAS,mBAAmB;AAClC,WAAS;AACT,MAAI,mBAAmB,MAAM,OAAQ,QAAO;AAC7C;AAGO,SAAS,sBACf,UACa;AACb,YAAU,IAAI,QAAQ;AACtB,MAAI,CAAC,SAAS;AACb,UAAM,QAAQ,WAAW;AACzB,WAAO,iBAAiB,UAAU,aAAa;AAC/C,cAAU,MAAM,OAAO,oBAAoB,UAAU,aAAa;AAAA,EACnE;AACA,SAAO,MAAM;AACZ,cAAU,OAAO,QAAQ;AACzB,QAAI,UAAU,OAAO,EAAG;AACxB,cAAU;AACV,cAAU;AAAA,EACX;AACD;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AnimationControls, AnimationOptions, DOMKeyframes, ElementTarget, Keyframes, ValueAnimationOptions } from "./types.js";
|
|
2
|
+
import { type Segment } from "../sequence/sequence.js";
|
|
3
|
+
import { type MotionValue } from "../value/motion-value.js";
|
|
4
|
+
export interface Animate {
|
|
5
|
+
/** Elements: a selector, an element or a list. Played by the browser where it can. */
|
|
6
|
+
(target: ElementTarget, keyframes: DOMKeyframes, options?: AnimationOptions): AnimationControls;
|
|
7
|
+
/** A motion value, from wherever it is now. */
|
|
8
|
+
<T>(value: MotionValue<T>, target: Keyframes<T>, options?: ValueAnimationOptions<T>): AnimationControls;
|
|
9
|
+
/** A number, a colour, a path… from one value to another, read in `onUpdate`. */
|
|
10
|
+
<T extends number | string>(from: T, to: Keyframes<T>, options: ValueAnimationOptions<T>): AnimationControls;
|
|
11
|
+
/** The keys of a plain object, written back into it. */
|
|
12
|
+
<T extends object>(object: T, keyframes: {
|
|
13
|
+
readonly [K in keyof T]?: Keyframes<T[K]>;
|
|
14
|
+
}, options?: ValueAnimationOptions<T>): AnimationControls;
|
|
15
|
+
/** A timeline of segments. */
|
|
16
|
+
(sequence: readonly Segment[], options?: AnimationOptions): AnimationControls;
|
|
17
|
+
}
|
|
18
|
+
/** `animate`, with selectors looked up inside `scope` (the whole document by default). */
|
|
19
|
+
export declare function createAnimate(scope?: () => ParentNode | null): Animate;
|
|
20
|
+
export declare const animate: Animate;
|
|
21
|
+
//# sourceMappingURL=animate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"animate.d.ts","sourceRoot":"","sources":["../../src/animate/animate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,SAAS,EACT,qBAAqB,EACrB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAmB,KAAK,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACvE,OAAO,EAEN,KAAK,WAAW,EAEhB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,OAAO;IACvB,sFAAsF;IACtF,CACC,MAAM,EAAE,aAAa,EACrB,SAAS,EAAE,YAAY,EACvB,OAAO,CAAC,EAAE,gBAAgB,GACxB,iBAAiB,CAAC;IACrB,+CAA+C;IAC/C,CAAC,CAAC,EACD,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,EACrB,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,EACpB,OAAO,CAAC,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAChC,iBAAiB,CAAC;IACrB,iFAAiF;IACjF,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EACzB,IAAI,EAAE,CAAC,EACP,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC,EAChB,OAAO,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAC/B,iBAAiB,CAAC;IACrB,wDAAwD;IACxD,CAAC,CAAC,SAAS,MAAM,EAChB,MAAM,EAAE,CAAC,EACT,SAAS,EAAE;QAAE,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KAAE,EACxD,OAAO,CAAC,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAChC,iBAAiB,CAAC;IACrB,8BAA8B;IAC9B,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,iBAAiB,CAAC;CAC9E;AAkCD,0FAA0F;AAC1F,wBAAgB,aAAa,CAAC,KAAK,CAAC,EAAE,MAAM,UAAU,GAAG,IAAI,GAAG,OAAO,CAwEtE;AAED,eAAO,MAAM,OAAO,EAAE,OAAyB,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { animateElements } from "./dom.js";
|
|
2
|
+
import { jsAnimation } from "./js-animation.js";
|
|
3
|
+
import { animateMotionValue, animateObject } from "./value.js";
|
|
4
|
+
import { animateSequence } from "../sequence/sequence.js";
|
|
5
|
+
import {
|
|
6
|
+
isMotionValue,
|
|
7
|
+
motionValue
|
|
8
|
+
} from "../value/motion-value.js";
|
|
9
|
+
function isElementList(target) {
|
|
10
|
+
return typeof target === "object" && target !== null && "length" in target && Array.from(target).every(
|
|
11
|
+
(item) => typeof Element !== "undefined" && item instanceof Element
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
function isSegment(item) {
|
|
15
|
+
return typeof item === "string" || Array.isArray(item) || typeof item === "object" && item !== null && Object.getPrototypeOf(item) === Object.prototype && "name" in item;
|
|
16
|
+
}
|
|
17
|
+
function isSequence(target, second) {
|
|
18
|
+
return Array.isArray(target) && (second === void 0 || !Array.isArray(second)) && target.length > 0 && target.every(isSegment);
|
|
19
|
+
}
|
|
20
|
+
function createAnimate(scope) {
|
|
21
|
+
function elementsOf(target) {
|
|
22
|
+
if (typeof target === "string") {
|
|
23
|
+
const root = scope?.() ?? (typeof document === "undefined" ? null : document);
|
|
24
|
+
return root ? Array.from(root.querySelectorAll(target)) : [];
|
|
25
|
+
}
|
|
26
|
+
if (typeof Element !== "undefined" && target instanceof Element)
|
|
27
|
+
return [target];
|
|
28
|
+
return Array.from(target);
|
|
29
|
+
}
|
|
30
|
+
function animateOne(target, keyframes, options = {}) {
|
|
31
|
+
if (isMotionValue(target)) {
|
|
32
|
+
return animateMotionValue(
|
|
33
|
+
target,
|
|
34
|
+
keyframes,
|
|
35
|
+
options
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
const domKeyframes = typeof keyframes === "object" && keyframes !== null && !Array.isArray(keyframes);
|
|
39
|
+
if (domKeyframes && (typeof target === "string" || typeof Element !== "undefined" && target instanceof Element || isElementList(target))) {
|
|
40
|
+
return animateElements(
|
|
41
|
+
elementsOf(target),
|
|
42
|
+
keyframes,
|
|
43
|
+
options,
|
|
44
|
+
jsAnimation
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
if (typeof target === "number" || typeof target === "string") {
|
|
48
|
+
return animateMotionValue(
|
|
49
|
+
motionValue(target),
|
|
50
|
+
keyframes,
|
|
51
|
+
options
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
return animateObject(
|
|
55
|
+
target,
|
|
56
|
+
keyframes,
|
|
57
|
+
options
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
function targetsOf(target) {
|
|
61
|
+
const isDom = typeof target === "string" || typeof Element !== "undefined" && target instanceof Element || isElementList(target);
|
|
62
|
+
return isDom ? elementsOf(target) : [target];
|
|
63
|
+
}
|
|
64
|
+
return ((target, second, options) => isSequence(target, second) ? animateSequence(
|
|
65
|
+
target,
|
|
66
|
+
second ?? {},
|
|
67
|
+
animateOne,
|
|
68
|
+
targetsOf
|
|
69
|
+
) : animateOne(target, second, options));
|
|
70
|
+
}
|
|
71
|
+
const animate = createAnimate();
|
|
72
|
+
export {
|
|
73
|
+
animate,
|
|
74
|
+
createAnimate
|
|
75
|
+
};
|
|
76
|
+
//# sourceMappingURL=animate.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/animate/animate.ts"],
|
|
4
|
+
"sourcesContent": ["import { animateElements } from \"#/animate/dom.ts\";\nimport { jsAnimation } from \"#/animate/js-animation.ts\";\nimport type {\n\tAnimationControls,\n\tAnimationOptions,\n\tDOMKeyframes,\n\tElementTarget,\n\tKeyframes,\n\tValueAnimationOptions,\n} from \"#/animate/types.ts\";\nimport { animateMotionValue, animateObject } from \"#/animate/value.ts\";\nimport { animateSequence, type Segment } from \"#/sequence/sequence.ts\";\nimport {\n\tisMotionValue,\n\ttype MotionValue,\n\tmotionValue,\n} from \"#/value/motion-value.ts\";\n\nexport interface Animate {\n\t/** Elements: a selector, an element or a list. Played by the browser where it can. */\n\t(\n\t\ttarget: ElementTarget,\n\t\tkeyframes: DOMKeyframes,\n\t\toptions?: AnimationOptions,\n\t): AnimationControls;\n\t/** A motion value, from wherever it is now. */\n\t<T>(\n\t\tvalue: MotionValue<T>,\n\t\ttarget: Keyframes<T>,\n\t\toptions?: ValueAnimationOptions<T>,\n\t): AnimationControls;\n\t/** A number, a colour, a path\u2026 from one value to another, read in `onUpdate`. */\n\t<T extends number | string>(\n\t\tfrom: T,\n\t\tto: Keyframes<T>,\n\t\toptions: ValueAnimationOptions<T>,\n\t): AnimationControls;\n\t/** The keys of a plain object, written back into it. */\n\t<T extends object>(\n\t\tobject: T,\n\t\tkeyframes: { readonly [K in keyof T]?: Keyframes<T[K]> },\n\t\toptions?: ValueAnimationOptions<T>,\n\t): AnimationControls;\n\t/** A timeline of segments. */\n\t(sequence: readonly Segment[], options?: AnimationOptions): AnimationControls;\n}\n\nfunction isElementList(target: unknown): target is ArrayLike<Element> {\n\treturn (\n\t\ttypeof target === \"object\" &&\n\t\ttarget !== null &&\n\t\t\"length\" in target &&\n\t\t(Array.from(target as ArrayLike<unknown>).every(\n\t\t\t(item) => typeof Element !== \"undefined\" && item instanceof Element,\n\t\t) as boolean)\n\t);\n}\n\n/** A label object is a plain `{ name }`: a form element also has a `name`. */\nfunction isSegment(item: unknown): boolean {\n\treturn (\n\t\ttypeof item === \"string\" ||\n\t\tArray.isArray(item) ||\n\t\t(typeof item === \"object\" &&\n\t\t\titem !== null &&\n\t\t\tObject.getPrototypeOf(item) === Object.prototype &&\n\t\t\t\"name\" in item)\n\t);\n}\n\nfunction isSequence(target: unknown, second: unknown): target is Segment[] {\n\treturn (\n\t\tArray.isArray(target) &&\n\t\t(second === undefined || !Array.isArray(second)) &&\n\t\ttarget.length > 0 &&\n\t\ttarget.every(isSegment)\n\t);\n}\n\n/** `animate`, with selectors looked up inside `scope` (the whole document by default). */\nexport function createAnimate(scope?: () => ParentNode | null): Animate {\n\tfunction elementsOf(target: unknown): readonly Element[] {\n\t\tif (typeof target === \"string\") {\n\t\t\tconst root =\n\t\t\t\tscope?.() ?? (typeof document === \"undefined\" ? null : document);\n\t\t\treturn root ? Array.from(root.querySelectorAll(target)) : [];\n\t\t}\n\t\tif (typeof Element !== \"undefined\" && target instanceof Element)\n\t\t\treturn [target];\n\t\treturn Array.from(target as ArrayLike<Element>);\n\t}\n\n\tfunction animateOne(\n\t\ttarget: unknown,\n\t\tkeyframes: unknown,\n\t\toptions: AnimationOptions = {},\n\t): AnimationControls {\n\t\tif (isMotionValue(target)) {\n\t\t\treturn animateMotionValue(\n\t\t\t\ttarget,\n\t\t\t\tkeyframes as Keyframes<unknown>,\n\t\t\t\toptions,\n\t\t\t);\n\t\t}\n\t\tconst domKeyframes =\n\t\t\ttypeof keyframes === \"object\" &&\n\t\t\tkeyframes !== null &&\n\t\t\t!Array.isArray(keyframes);\n\t\tif (\n\t\t\tdomKeyframes &&\n\t\t\t(typeof target === \"string\" ||\n\t\t\t\t(typeof Element !== \"undefined\" && target instanceof Element) ||\n\t\t\t\tisElementList(target))\n\t\t) {\n\t\t\treturn animateElements(\n\t\t\t\telementsOf(target),\n\t\t\t\tkeyframes as DOMKeyframes,\n\t\t\t\toptions,\n\t\t\t\tjsAnimation,\n\t\t\t);\n\t\t}\n\t\tif (typeof target === \"number\" || typeof target === \"string\") {\n\t\t\treturn animateMotionValue(\n\t\t\t\tmotionValue(target),\n\t\t\t\tkeyframes as Keyframes<unknown>,\n\t\t\t\toptions,\n\t\t\t);\n\t\t}\n\t\treturn animateObject(\n\t\t\ttarget as object,\n\t\t\tkeyframes as Record<string, Keyframes<unknown>>,\n\t\t\toptions,\n\t\t);\n\t}\n\n\tfunction targetsOf(target: unknown): readonly object[] {\n\t\tconst isDom =\n\t\t\ttypeof target === \"string\" ||\n\t\t\t(typeof Element !== \"undefined\" && target instanceof Element) ||\n\t\t\tisElementList(target);\n\t\treturn isDom ? elementsOf(target) : [target as object];\n\t}\n\n\treturn ((target: unknown, second?: unknown, options?: AnimationOptions) =>\n\t\tisSequence(target, second)\n\t\t\t? animateSequence(\n\t\t\t\t\ttarget,\n\t\t\t\t\t(second as AnimationOptions) ?? {},\n\t\t\t\t\tanimateOne,\n\t\t\t\t\ttargetsOf,\n\t\t\t\t)\n\t\t\t: animateOne(target, second, options)) as Animate;\n}\n\nexport const animate: Animate = createAnimate();\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,uBAAuB;AAChC,SAAS,mBAAmB;AAS5B,SAAS,oBAAoB,qBAAqB;AAClD,SAAS,uBAAqC;AAC9C;AAAA,EACC;AAAA,EAEA;AAAA,OACM;AA+BP,SAAS,cAAc,QAA+C;AACrE,SACC,OAAO,WAAW,YAClB,WAAW,QACX,YAAY,UACX,MAAM,KAAK,MAA4B,EAAE;AAAA,IACzC,CAAC,SAAS,OAAO,YAAY,eAAe,gBAAgB;AAAA,EAC7D;AAEF;AAGA,SAAS,UAAU,MAAwB;AAC1C,SACC,OAAO,SAAS,YAChB,MAAM,QAAQ,IAAI,KACjB,OAAO,SAAS,YAChB,SAAS,QACT,OAAO,eAAe,IAAI,MAAM,OAAO,aACvC,UAAU;AAEb;AAEA,SAAS,WAAW,QAAiB,QAAsC;AAC1E,SACC,MAAM,QAAQ,MAAM,MACnB,WAAW,UAAa,CAAC,MAAM,QAAQ,MAAM,MAC9C,OAAO,SAAS,KAChB,OAAO,MAAM,SAAS;AAExB;AAGO,SAAS,cAAc,OAA0C;AACvE,WAAS,WAAW,QAAqC;AACxD,QAAI,OAAO,WAAW,UAAU;AAC/B,YAAM,OACL,QAAQ,MAAM,OAAO,aAAa,cAAc,OAAO;AACxD,aAAO,OAAO,MAAM,KAAK,KAAK,iBAAiB,MAAM,CAAC,IAAI,CAAC;AAAA,IAC5D;AACA,QAAI,OAAO,YAAY,eAAe,kBAAkB;AACvD,aAAO,CAAC,MAAM;AACf,WAAO,MAAM,KAAK,MAA4B;AAAA,EAC/C;AAEA,WAAS,WACR,QACA,WACA,UAA4B,CAAC,GACT;AACpB,QAAI,cAAc,MAAM,GAAG;AAC1B,aAAO;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,MACD;AAAA,IACD;AACA,UAAM,eACL,OAAO,cAAc,YACrB,cAAc,QACd,CAAC,MAAM,QAAQ,SAAS;AACzB,QACC,iBACC,OAAO,WAAW,YACjB,OAAO,YAAY,eAAe,kBAAkB,WACrD,cAAc,MAAM,IACpB;AACD,aAAO;AAAA,QACN,WAAW,MAAM;AAAA,QACjB;AAAA,QACA;AAAA,QACA;AAAA,MACD;AAAA,IACD;AACA,QAAI,OAAO,WAAW,YAAY,OAAO,WAAW,UAAU;AAC7D,aAAO;AAAA,QACN,YAAY,MAAM;AAAA,QAClB;AAAA,QACA;AAAA,MACD;AAAA,IACD;AACA,WAAO;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAAA,EACD;AAEA,WAAS,UAAU,QAAoC;AACtD,UAAM,QACL,OAAO,WAAW,YACjB,OAAO,YAAY,eAAe,kBAAkB,WACrD,cAAc,MAAM;AACrB,WAAO,QAAQ,WAAW,MAAM,IAAI,CAAC,MAAgB;AAAA,EACtD;AAEA,UAAQ,CAAC,QAAiB,QAAkB,YAC3C,WAAW,QAAQ,MAAM,IACtB;AAAA,IACA;AAAA,IACC,UAA+B,CAAC;AAAA,IACjC;AAAA,IACA;AAAA,EACD,IACC,WAAW,QAAQ,QAAQ,OAAO;AACvC;AAEO,MAAM,UAAmB,cAAc;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AnimationControls } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* `finished` and the `then` that makes controls awaitable:
|
|
4
|
+
* `await animate(…)` waits for the end, the way Motion's controls do.
|
|
5
|
+
*/
|
|
6
|
+
export declare function thenable(finished: Promise<void>): Pick<AnimationControls, "finished" | "then">;
|
|
7
|
+
/**
|
|
8
|
+
* One set of controls over many animations: the properties of one element,
|
|
9
|
+
* the elements of a selector, the segments of a sequence. They share a clock
|
|
10
|
+
* (every child's `time` counts from the same start), so scrubbing the group
|
|
11
|
+
* scrubs each child at the same moment.
|
|
12
|
+
*/
|
|
13
|
+
export declare function groupControls(children: readonly AnimationControls[]): AnimationControls;
|
|
14
|
+
/** The controls of an animation that had nothing to animate, or jumped: already finished. */
|
|
15
|
+
export declare function finishedControls(): AnimationControls;
|
|
16
|
+
//# sourceMappingURL=controls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controls.d.ts","sourceRoot":"","sources":["../../src/animate/controls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAkB,MAAM,oBAAoB,CAAC;AAE5E;;;GAGG;AACH,wBAAgB,QAAQ,CACvB,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,GACrB,IAAI,CAAC,iBAAiB,EAAE,UAAU,GAAG,MAAM,CAAC,CAM9C;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC5B,QAAQ,EAAE,SAAS,iBAAiB,EAAE,GACpC,iBAAiB,CA8CnB;AAED,6FAA6F;AAC7F,wBAAgB,gBAAgB,IAAI,iBAAiB,CAEpD"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
function thenable(finished) {
|
|
2
|
+
return {
|
|
3
|
+
finished,
|
|
4
|
+
// biome-ignore lint/suspicious/noThenProperty: controls are awaitable on purpose.
|
|
5
|
+
then: (onFulfilled, onRejected) => finished.then(onFulfilled, onRejected)
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
function groupControls(children) {
|
|
9
|
+
const finished = Promise.all(children.map((child) => child.finished)).then(
|
|
10
|
+
() => void 0
|
|
11
|
+
);
|
|
12
|
+
function each(action) {
|
|
13
|
+
return () => {
|
|
14
|
+
for (const child of children) action(child);
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
...thenable(finished),
|
|
19
|
+
get time() {
|
|
20
|
+
return Math.max(0, ...children.map((child) => child.time));
|
|
21
|
+
},
|
|
22
|
+
set time(seconds) {
|
|
23
|
+
for (const child of children) child.time = seconds;
|
|
24
|
+
},
|
|
25
|
+
get speed() {
|
|
26
|
+
return children[0]?.speed ?? 1;
|
|
27
|
+
},
|
|
28
|
+
set speed(rate) {
|
|
29
|
+
for (const child of children) child.speed = rate;
|
|
30
|
+
},
|
|
31
|
+
get duration() {
|
|
32
|
+
return Math.max(0, ...children.map((child) => child.duration));
|
|
33
|
+
},
|
|
34
|
+
get state() {
|
|
35
|
+
const states = new Set(children.map((child) => child.state));
|
|
36
|
+
if (states.has("running")) return "running";
|
|
37
|
+
return states.has("paused") ? "paused" : "finished";
|
|
38
|
+
},
|
|
39
|
+
play: each((child) => child.play()),
|
|
40
|
+
pause: each((child) => child.pause()),
|
|
41
|
+
stop: each((child) => child.stop()),
|
|
42
|
+
cancel: each((child) => child.cancel()),
|
|
43
|
+
complete: each((child) => child.complete()),
|
|
44
|
+
attachTimeline(timeline) {
|
|
45
|
+
const attached = children.filter(
|
|
46
|
+
(child) => child.attachTimeline(timeline)
|
|
47
|
+
);
|
|
48
|
+
if (attached.length === children.length) return true;
|
|
49
|
+
for (const child of attached) child.attachTimeline(document.timeline);
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function finishedControls() {
|
|
55
|
+
return groupControls([]);
|
|
56
|
+
}
|
|
57
|
+
export {
|
|
58
|
+
finishedControls,
|
|
59
|
+
groupControls,
|
|
60
|
+
thenable
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=controls.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/animate/controls.ts"],
|
|
4
|
+
"sourcesContent": ["import type { AnimationControls, AnimationState } from \"#/animate/types.ts\";\n\n/**\n * `finished` and the `then` that makes controls awaitable:\n * `await animate(\u2026)` waits for the end, the way Motion's controls do.\n */\nexport function thenable(\n\tfinished: Promise<void>,\n): Pick<AnimationControls, \"finished\" | \"then\"> {\n\treturn {\n\t\tfinished,\n\t\t// biome-ignore lint/suspicious/noThenProperty: controls are awaitable on purpose.\n\t\tthen: (onFulfilled, onRejected) => finished.then(onFulfilled, onRejected),\n\t};\n}\n\n/**\n * One set of controls over many animations: the properties of one element,\n * the elements of a selector, the segments of a sequence. They share a clock\n * (every child's `time` counts from the same start), so scrubbing the group\n * scrubs each child at the same moment.\n */\nexport function groupControls(\n\tchildren: readonly AnimationControls[],\n): AnimationControls {\n\tconst finished = Promise.all(children.map((child) => child.finished)).then(\n\t\t() => undefined,\n\t);\n\tfunction each(action: (child: AnimationControls) => void) {\n\t\treturn () => {\n\t\t\tfor (const child of children) action(child);\n\t\t};\n\t}\n\treturn {\n\t\t...thenable(finished),\n\t\tget time() {\n\t\t\treturn Math.max(0, ...children.map((child) => child.time));\n\t\t},\n\t\tset time(seconds) {\n\t\t\tfor (const child of children) child.time = seconds;\n\t\t},\n\t\tget speed() {\n\t\t\treturn children[0]?.speed ?? 1;\n\t\t},\n\t\tset speed(rate) {\n\t\t\tfor (const child of children) child.speed = rate;\n\t\t},\n\t\tget duration() {\n\t\t\treturn Math.max(0, ...children.map((child) => child.duration));\n\t\t},\n\t\tget state(): AnimationState {\n\t\t\tconst states = new Set(children.map((child) => child.state));\n\t\t\tif (states.has(\"running\")) return \"running\";\n\t\t\treturn states.has(\"paused\") ? \"paused\" : \"finished\";\n\t\t},\n\t\tplay: each((child) => child.play()),\n\t\tpause: each((child) => child.pause()),\n\t\tstop: each((child) => child.stop()),\n\t\tcancel: each((child) => child.cancel()),\n\t\tcomplete: each((child) => child.complete()),\n\t\tattachTimeline(timeline) {\n\t\t\tconst attached = children.filter((child) =>\n\t\t\t\tchild.attachTimeline(timeline),\n\t\t\t);\n\t\t\tif (attached.length === children.length) return true;\n\t\t\t// All or nothing: a half-native group would drift from its JS half.\n\t\t\tfor (const child of attached) child.attachTimeline(document.timeline);\n\t\t\treturn false;\n\t\t},\n\t};\n}\n\n/** The controls of an animation that had nothing to animate, or jumped: already finished. */\nexport function finishedControls(): AnimationControls {\n\treturn groupControls([]);\n}\n"],
|
|
5
|
+
"mappings": "AAMO,SAAS,SACf,UAC+C;AAC/C,SAAO;AAAA,IACN;AAAA;AAAA,IAEA,MAAM,CAAC,aAAa,eAAe,SAAS,KAAK,aAAa,UAAU;AAAA,EACzE;AACD;AAQO,SAAS,cACf,UACoB;AACpB,QAAM,WAAW,QAAQ,IAAI,SAAS,IAAI,CAAC,UAAU,MAAM,QAAQ,CAAC,EAAE;AAAA,IACrE,MAAM;AAAA,EACP;AACA,WAAS,KAAK,QAA4C;AACzD,WAAO,MAAM;AACZ,iBAAW,SAAS,SAAU,QAAO,KAAK;AAAA,IAC3C;AAAA,EACD;AACA,SAAO;AAAA,IACN,GAAG,SAAS,QAAQ;AAAA,IACpB,IAAI,OAAO;AACV,aAAO,KAAK,IAAI,GAAG,GAAG,SAAS,IAAI,CAAC,UAAU,MAAM,IAAI,CAAC;AAAA,IAC1D;AAAA,IACA,IAAI,KAAK,SAAS;AACjB,iBAAW,SAAS,SAAU,OAAM,OAAO;AAAA,IAC5C;AAAA,IACA,IAAI,QAAQ;AACX,aAAO,SAAS,CAAC,GAAG,SAAS;AAAA,IAC9B;AAAA,IACA,IAAI,MAAM,MAAM;AACf,iBAAW,SAAS,SAAU,OAAM,QAAQ;AAAA,IAC7C;AAAA,IACA,IAAI,WAAW;AACd,aAAO,KAAK,IAAI,GAAG,GAAG,SAAS,IAAI,CAAC,UAAU,MAAM,QAAQ,CAAC;AAAA,IAC9D;AAAA,IACA,IAAI,QAAwB;AAC3B,YAAM,SAAS,IAAI,IAAI,SAAS,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC;AAC3D,UAAI,OAAO,IAAI,SAAS,EAAG,QAAO;AAClC,aAAO,OAAO,IAAI,QAAQ,IAAI,WAAW;AAAA,IAC1C;AAAA,IACA,MAAM,KAAK,CAAC,UAAU,MAAM,KAAK,CAAC;AAAA,IAClC,OAAO,KAAK,CAAC,UAAU,MAAM,MAAM,CAAC;AAAA,IACpC,MAAM,KAAK,CAAC,UAAU,MAAM,KAAK,CAAC;AAAA,IAClC,QAAQ,KAAK,CAAC,UAAU,MAAM,OAAO,CAAC;AAAA,IACtC,UAAU,KAAK,CAAC,UAAU,MAAM,SAAS,CAAC;AAAA,IAC1C,eAAe,UAAU;AACxB,YAAM,WAAW,SAAS;AAAA,QAAO,CAAC,UACjC,MAAM,eAAe,QAAQ;AAAA,MAC9B;AACA,UAAI,SAAS,WAAW,SAAS,OAAQ,QAAO;AAEhD,iBAAW,SAAS,SAAU,OAAM,eAAe,SAAS,QAAQ;AACpE,aAAO;AAAA,IACR;AAAA,EACD;AACD;AAGO,SAAS,mBAAsC;AACrD,SAAO,cAAc,CAAC,CAAC;AACxB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { JsAnimation, JsAnimationConfig } from "./js-animation.js";
|
|
2
|
+
import type { AnimationControls, AnimationOptions, DOMKeyframes, KeyframeValue } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Plays what the browser can't (SVG attributes, CSS variables) on the frame
|
|
5
|
+
* loop. Injected, so an entry that only needs the browser's half never loads
|
|
6
|
+
* the interpolators.
|
|
7
|
+
*/
|
|
8
|
+
export type ScriptedChannel = (config: JsAnimationConfig<KeyframeValue>) => JsAnimation<KeyframeValue>;
|
|
9
|
+
/** Every key of every element, each its own animation, under one set of controls. */
|
|
10
|
+
export declare function animateElements(elements: readonly Element[], keyframes: DOMKeyframes, options: AnimationOptions, scripted?: ScriptedChannel): AnimationControls;
|
|
11
|
+
//# sourceMappingURL=dom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../../src/animate/dom.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAUhF,OAAO,KAAK,EACX,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,MAAM,oBAAoB,CAAC;AAuB5B;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,CAC7B,MAAM,EAAE,iBAAiB,CAAC,aAAa,CAAC,KACpC,WAAW,CAAC,aAAa,CAAC,CAAC;AAmGhC,qFAAqF;AACrF,wBAAgB,eAAe,CAC9B,QAAQ,EAAE,SAAS,OAAO,EAAE,EAC5B,SAAS,EAAE,YAAY,EACvB,OAAO,EAAE,gBAAgB,EACzB,QAAQ,CAAC,EAAE,eAAe,GACxB,iBAAiB,CAanB"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { shouldReduceMotion } from "../accessibility/reduced-motion.js";
|
|
2
|
+
import { finishedControls, groupControls } from "./controls.js";
|
|
3
|
+
import {
|
|
4
|
+
beginTransform,
|
|
5
|
+
commitTransform,
|
|
6
|
+
committedTransform,
|
|
7
|
+
cssName,
|
|
8
|
+
isMovement,
|
|
9
|
+
TRANSFORMS,
|
|
10
|
+
withUnit
|
|
11
|
+
} from "./style.js";
|
|
12
|
+
import { waapiAnimation } from "./waapi.js";
|
|
13
|
+
const SVG_ATTRIBUTES = {
|
|
14
|
+
d: "d",
|
|
15
|
+
points: "points",
|
|
16
|
+
viewBox: "viewBox",
|
|
17
|
+
cx: "cx",
|
|
18
|
+
cy: "cy",
|
|
19
|
+
r: "r",
|
|
20
|
+
rx: "rx",
|
|
21
|
+
ry: "ry",
|
|
22
|
+
x1: "x1",
|
|
23
|
+
y1: "y1",
|
|
24
|
+
x2: "x2",
|
|
25
|
+
y2: "y2",
|
|
26
|
+
attrX: "x",
|
|
27
|
+
attrY: "y",
|
|
28
|
+
attrWidth: "width",
|
|
29
|
+
attrHeight: "height"
|
|
30
|
+
};
|
|
31
|
+
const running = /* @__PURE__ */ new WeakMap();
|
|
32
|
+
function channelOf(element, key) {
|
|
33
|
+
const style = element.style;
|
|
34
|
+
const attribute = element instanceof SVGElement ? SVG_ATTRIBUTES[key] : void 0;
|
|
35
|
+
if (TRANSFORMS[key]) {
|
|
36
|
+
return {
|
|
37
|
+
read: () => committedTransform(element, key),
|
|
38
|
+
write: (value) => commitTransform(element, key, value),
|
|
39
|
+
native: true
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (attribute) {
|
|
43
|
+
return {
|
|
44
|
+
read: () => element.getAttribute(attribute) ?? "",
|
|
45
|
+
write: (value) => element.setAttribute(attribute, String(value)),
|
|
46
|
+
native: false
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const name = cssName(key);
|
|
50
|
+
return {
|
|
51
|
+
read: () => getComputedStyle(element).getPropertyValue(name).trim(),
|
|
52
|
+
write: (value) => style.setProperty(name, withUnit(key, value)),
|
|
53
|
+
// An unregistered custom property only animates discretely in the browser.
|
|
54
|
+
native: !key.startsWith("--")
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function asTarget(read, target) {
|
|
58
|
+
return typeof target === "number" && typeof read === "string" ? Number.parseFloat(read) || 0 : read;
|
|
59
|
+
}
|
|
60
|
+
function animateChannel(element, key, value, options, delay, scripted) {
|
|
61
|
+
const channel = channelOf(element, key);
|
|
62
|
+
const list = Array.isArray(value) ? value : [value];
|
|
63
|
+
const target = list.at(-1);
|
|
64
|
+
const animations = running.get(element) ?? /* @__PURE__ */ new Map();
|
|
65
|
+
running.set(element, animations);
|
|
66
|
+
const previous = animations.get(key);
|
|
67
|
+
const now = previous?.current();
|
|
68
|
+
previous?.stop();
|
|
69
|
+
const from = now?.value ?? asTarget(channel.read(), target);
|
|
70
|
+
const keyframes = list.length > 1 ? list : [from, target];
|
|
71
|
+
if (shouldReduceMotion(options.reducedMotion) && isMovement(key) || !channel.native && !scripted) {
|
|
72
|
+
channel.write(target);
|
|
73
|
+
return finishedControls();
|
|
74
|
+
}
|
|
75
|
+
const reduced = shouldReduceMotion(options.reducedMotion) ? { ...options, type: "tween", bounce: 0 } : options;
|
|
76
|
+
const config = {
|
|
77
|
+
keyframes,
|
|
78
|
+
options: reduced,
|
|
79
|
+
delay,
|
|
80
|
+
velocity: list.length > 1 ? 0 : now?.velocity ?? 0
|
|
81
|
+
};
|
|
82
|
+
const transform = TRANSFORMS[key];
|
|
83
|
+
if (transform) beginTransform(element, key);
|
|
84
|
+
const animation = channel.native ? waapiAnimation({
|
|
85
|
+
...config,
|
|
86
|
+
element,
|
|
87
|
+
property: transform?.property ?? key,
|
|
88
|
+
format: (frame) => transform ? transform.alone(withUnit(key, frame)) : withUnit(key, frame),
|
|
89
|
+
composite: transform ? "add" : "replace",
|
|
90
|
+
commit: channel.write
|
|
91
|
+
}) : scripted({ ...config, apply: channel.write });
|
|
92
|
+
animations.set(key, animation);
|
|
93
|
+
animation.finished.then(() => {
|
|
94
|
+
if (animations.get(key) === animation) animations.delete(key);
|
|
95
|
+
});
|
|
96
|
+
return animation;
|
|
97
|
+
}
|
|
98
|
+
function animateElements(elements, keyframes, options, scripted) {
|
|
99
|
+
const { delay } = options;
|
|
100
|
+
return groupControls(
|
|
101
|
+
elements.flatMap((element, index) => {
|
|
102
|
+
const start = typeof delay === "function" ? delay(index, elements.length) : delay ?? 0;
|
|
103
|
+
return Object.entries(keyframes).map(
|
|
104
|
+
([key, value]) => animateChannel(element, key, value, options, start, scripted)
|
|
105
|
+
);
|
|
106
|
+
})
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
export {
|
|
110
|
+
animateElements
|
|
111
|
+
};
|
|
112
|
+
//# sourceMappingURL=dom.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/animate/dom.ts"],
|
|
4
|
+
"sourcesContent": ["import { shouldReduceMotion } from \"#/accessibility/reduced-motion.ts\";\nimport { finishedControls, groupControls } from \"#/animate/controls.ts\";\nimport type { JsAnimation, JsAnimationConfig } from \"#/animate/js-animation.ts\";\nimport {\n\tbeginTransform,\n\tcommitTransform,\n\tcommittedTransform,\n\tcssName,\n\tisMovement,\n\tTRANSFORMS,\n\twithUnit,\n} from \"#/animate/style.ts\";\nimport type {\n\tAnimationControls,\n\tAnimationOptions,\n\tDOMKeyframes,\n\tKeyframeValue,\n} from \"#/animate/types.ts\";\nimport { waapiAnimation } from \"#/animate/waapi.ts\";\n\n/** Attributes an SVG element animates in JavaScript: `d` morphs, the rest are geometry. */\nconst SVG_ATTRIBUTES: Readonly<Record<string, string>> = {\n\td: \"d\",\n\tpoints: \"points\",\n\tviewBox: \"viewBox\",\n\tcx: \"cx\",\n\tcy: \"cy\",\n\tr: \"r\",\n\trx: \"rx\",\n\try: \"ry\",\n\tx1: \"x1\",\n\ty1: \"y1\",\n\tx2: \"x2\",\n\ty2: \"y2\",\n\tattrX: \"x\",\n\tattrY: \"y\",\n\tattrWidth: \"width\",\n\tattrHeight: \"height\",\n};\n\n/**\n * Plays what the browser can't (SVG attributes, CSS variables) on the frame\n * loop. Injected, so an entry that only needs the browser's half never loads\n * the interpolators.\n */\nexport type ScriptedChannel = (\n\tconfig: JsAnimationConfig<KeyframeValue>,\n) => JsAnimation<KeyframeValue>;\n\nconst running = new WeakMap<Element, Map<string, JsAnimation<KeyframeValue>>>();\n\ninterface Channel {\n\tread(): KeyframeValue;\n\twrite(value: KeyframeValue): void;\n\treadonly native: boolean;\n}\n\nfunction channelOf(element: Element, key: string): Channel {\n\tconst style = (element as HTMLElement).style;\n\tconst attribute =\n\t\telement instanceof SVGElement ? SVG_ATTRIBUTES[key] : undefined;\n\tif (TRANSFORMS[key]) {\n\t\treturn {\n\t\t\tread: () => committedTransform(element, key),\n\t\t\twrite: (value) => commitTransform(element, key, value),\n\t\t\tnative: true,\n\t\t};\n\t}\n\tif (attribute) {\n\t\treturn {\n\t\t\tread: () => element.getAttribute(attribute) ?? \"\",\n\t\t\twrite: (value) => element.setAttribute(attribute, String(value)),\n\t\t\tnative: false,\n\t\t};\n\t}\n\tconst name = cssName(key);\n\treturn {\n\t\tread: () => getComputedStyle(element).getPropertyValue(name).trim(),\n\t\twrite: (value) => style.setProperty(name, withUnit(key, value)),\n\t\t// An unregistered custom property only animates discretely in the browser.\n\t\tnative: !key.startsWith(\"--\"),\n\t};\n}\n\nfunction asTarget(read: KeyframeValue, target: KeyframeValue): KeyframeValue {\n\treturn typeof target === \"number\" && typeof read === \"string\"\n\t\t? Number.parseFloat(read) || 0\n\t\t: read;\n}\n\nfunction animateChannel(\n\telement: Element,\n\tkey: string,\n\tvalue: KeyframeValue | readonly KeyframeValue[],\n\toptions: AnimationOptions,\n\tdelay: number,\n\tscripted: ScriptedChannel | undefined,\n): AnimationControls {\n\tconst channel = channelOf(element, key);\n\tconst list: readonly KeyframeValue[] = Array.isArray(value) ? value : [value];\n\tconst target = list.at(-1) as KeyframeValue;\n\tconst animations = running.get(element) ?? new Map();\n\trunning.set(element, animations);\n\tconst previous = animations.get(key);\n\tconst now = previous?.current();\n\tprevious?.stop();\n\tconst from = now?.value ?? asTarget(channel.read(), target);\n\tconst keyframes = list.length > 1 ? list : [from, target];\n\tif (\n\t\t(shouldReduceMotion(options.reducedMotion) && isMovement(key)) ||\n\t\t(!channel.native && !scripted)\n\t) {\n\t\tchannel.write(target);\n\t\treturn finishedControls();\n\t}\n\tconst reduced = shouldReduceMotion(options.reducedMotion)\n\t\t? { ...options, type: \"tween\" as const, bounce: 0 }\n\t\t: options;\n\tconst config = {\n\t\tkeyframes,\n\t\toptions: reduced,\n\t\tdelay,\n\t\tvelocity: list.length > 1 ? 0 : (now?.velocity ?? 0),\n\t};\n\tconst transform = TRANSFORMS[key];\n\tif (transform) beginTransform(element, key);\n\tconst animation = channel.native\n\t\t? waapiAnimation({\n\t\t\t\t...config,\n\t\t\t\telement,\n\t\t\t\tproperty: transform?.property ?? key,\n\t\t\t\tformat: (frame) =>\n\t\t\t\t\ttransform\n\t\t\t\t\t\t? transform.alone(withUnit(key, frame))\n\t\t\t\t\t\t: withUnit(key, frame),\n\t\t\t\tcomposite: transform ? \"add\" : \"replace\",\n\t\t\t\tcommit: channel.write,\n\t\t\t})\n\t\t: (scripted as ScriptedChannel)({ ...config, apply: channel.write });\n\tanimations.set(key, animation);\n\tanimation.finished.then(() => {\n\t\tif (animations.get(key) === animation) animations.delete(key);\n\t});\n\treturn animation;\n}\n\n/** Every key of every element, each its own animation, under one set of controls. */\nexport function animateElements(\n\telements: readonly Element[],\n\tkeyframes: DOMKeyframes,\n\toptions: AnimationOptions,\n\tscripted?: ScriptedChannel,\n): AnimationControls {\n\tconst { delay } = options;\n\treturn groupControls(\n\t\telements.flatMap((element, index) => {\n\t\t\tconst start =\n\t\t\t\ttypeof delay === \"function\"\n\t\t\t\t\t? delay(index, elements.length)\n\t\t\t\t\t: (delay ?? 0);\n\t\t\treturn Object.entries(keyframes).map(([key, value]) =>\n\t\t\t\tanimateChannel(element, key, value, options, start, scripted),\n\t\t\t);\n\t\t}),\n\t);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { AnimationControls, AnimationOptions } from "./types.js";
|
|
2
|
+
export interface JsAnimationConfig<T> {
|
|
3
|
+
/** At least two: the caller resolves a lone target against the current value. */
|
|
4
|
+
readonly keyframes: readonly T[];
|
|
5
|
+
readonly options: AnimationOptions;
|
|
6
|
+
/** Seconds. */
|
|
7
|
+
readonly delay: number;
|
|
8
|
+
/** The velocity the value has now, carried into a spring. */
|
|
9
|
+
readonly velocity: number;
|
|
10
|
+
apply(value: T): void;
|
|
11
|
+
onComplete?(): void;
|
|
12
|
+
}
|
|
13
|
+
export interface JsAnimation<T> extends AnimationControls {
|
|
14
|
+
/** The value and velocity at the current time: where an interruption starts from. */
|
|
15
|
+
current(): {
|
|
16
|
+
readonly value: T;
|
|
17
|
+
readonly velocity: number;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* An animation stepped by the frame loop: plain values, objects, SVG
|
|
22
|
+
* attributes and CSS variables — whatever the Web Animations API can't play.
|
|
23
|
+
*/
|
|
24
|
+
export declare function jsAnimation<T>(config: JsAnimationConfig<T>): JsAnimation<T>;
|
|
25
|
+
//# sourceMappingURL=js-animation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"js-animation.d.ts","sourceRoot":"","sources":["../../src/animate/js-animation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,iBAAiB,EACjB,gBAAgB,EAEhB,MAAM,oBAAoB,CAAC;AAI5B,MAAM,WAAW,iBAAiB,CAAC,CAAC;IACnC,iFAAiF;IACjF,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,eAAe;IACf,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IACtB,UAAU,CAAC,IAAI,IAAI,CAAC;CACpB;AAED,MAAM,WAAW,WAAW,CAAC,CAAC,CAAE,SAAQ,iBAAiB;IACxD,qFAAqF;IACrF,OAAO,IAAI;QAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;QAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5D;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAuH3E"}
|