@ng-native/fabric 0.0.1 → 0.1.0

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.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +54 -1
  3. package/dist/color-mix.d.ts +20 -0
  4. package/dist/color-mix.js +306 -0
  5. package/dist/color-mix.js.map +1 -0
  6. package/dist/css.d.ts +615 -0
  7. package/dist/css.js +1405 -0
  8. package/dist/css.js.map +1 -0
  9. package/dist/defer-triggers.d.ts +53 -0
  10. package/dist/defer-triggers.js +184 -0
  11. package/dist/defer-triggers.js.map +1 -0
  12. package/dist/engine.d.ts +1066 -0
  13. package/dist/engine.js +2764 -0
  14. package/dist/engine.js.map +1 -0
  15. package/dist/fabric.d.ts +20 -0
  16. package/dist/fabric.js +44 -0
  17. package/dist/fabric.js.map +1 -0
  18. package/dist/forms-inputs.d.ts +15 -0
  19. package/dist/forms-inputs.js +54 -0
  20. package/dist/forms-inputs.js.map +1 -0
  21. package/dist/host.d.ts +133 -0
  22. package/dist/host.js +55 -0
  23. package/dist/host.js.map +1 -0
  24. package/dist/index.d.ts +20 -0
  25. package/dist/index.js +21 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/inline-token.d.ts +12 -0
  28. package/dist/inline-token.js +52 -0
  29. package/dist/inline-token.js.map +1 -0
  30. package/dist/inline-transform.d.ts +14 -0
  31. package/dist/inline-transform.js +41 -0
  32. package/dist/inline-transform.js.map +1 -0
  33. package/dist/native-drive.d.ts +100 -0
  34. package/dist/native-drive.js +318 -0
  35. package/dist/native-drive.js.map +1 -0
  36. package/dist/relative-colour.d.ts +17 -0
  37. package/dist/relative-colour.js +71 -0
  38. package/dist/relative-colour.js.map +1 -0
  39. package/dist/scroll-animation.d.ts +41 -0
  40. package/dist/scroll-animation.js +169 -0
  41. package/dist/scroll-animation.js.map +1 -0
  42. package/dist/transition.d.ts +139 -0
  43. package/dist/transition.js +569 -0
  44. package/dist/transition.js.map +1 -0
  45. package/package.json +43 -3
@@ -0,0 +1,169 @@
1
+ import { at } from "./transition.js";
2
+ /** What a range with no end, or a percentage of one, stands in for until the scroll view says. */
3
+ const UNKNOWN_EXTENT = 1e6;
4
+ /** Points per eased segment: enough that the curve between them reads as the curve. */
5
+ const SAMPLES = 12;
6
+ /** The transform groups in the order CSS applies them, as the engine composes them. */
7
+ const GROUPS = ['__translate', '__rotate', '__scale', 'transform'];
8
+ /** Where the range starts and ends on the scroll view's offset. */
9
+ export function rangeOf(spec, extent) {
10
+ const end = (value, otherwise) => {
11
+ if (value === undefined)
12
+ return otherwise;
13
+ if (typeof value === 'number')
14
+ return value;
15
+ return (parseFloat(value) / 100) * (extent ?? UNKNOWN_EXTENT);
16
+ };
17
+ return [end(spec.range?.start, 0), end(spec.range?.end, extent ?? UNKNOWN_EXTENT)];
18
+ }
19
+ /**
20
+ * The channels for an animation's tracks over a range, and the properties it cannot drive
21
+ * natively, which the caller holds at their first frame.
22
+ */
23
+ export function scrollChannels(tracks, spec, resting, range) {
24
+ const along = (track, pick, rest) => laid(track, pick, rest, spec, range);
25
+ const held = [];
26
+ let opacity;
27
+ const transform = [];
28
+ for (const [property, track] of tracks) {
29
+ if (property === 'opacity') {
30
+ opacity =
31
+ along(track, (value) => (typeof value === 'number' ? value : null), numberOr(resting['opacity'], 1)) ?? undefined;
32
+ if (!opacity)
33
+ held.push(property);
34
+ }
35
+ else if (!GROUPS.includes(property)) {
36
+ held.push(property);
37
+ }
38
+ }
39
+ for (const group of GROUPS) {
40
+ const track = tracks.get(group);
41
+ const channels = track ? drivenGroup(track, resting[group], along) : heldGroup(resting[group]);
42
+ if (channels === null)
43
+ held.push(group);
44
+ else
45
+ transform.push(...channels);
46
+ }
47
+ const anyDriven = transform.some((channel) => 'range' in channel);
48
+ return {
49
+ channels: { ...(opacity ? { opacity } : {}), transform: anyDriven ? transform : [] },
50
+ held,
51
+ };
52
+ }
53
+ /** One transform group's entries, each driven along the scroll; null if their shapes differ. */
54
+ function drivenGroup(track, resting, along) {
55
+ const shapes = track.map((point) => (Array.isArray(point.value) ? point.value : null));
56
+ const shape = shapes.find((entries) => entries?.length) ?? [];
57
+ const keys = shape.map((entry) => Object.keys(entry)[0]);
58
+ const channels = [];
59
+ for (const [index, key] of keys.entries()) {
60
+ const identity = key.startsWith('scale') ? 1 : 0;
61
+ const pick = (value) => {
62
+ const entries = Array.isArray(value) ? value : null;
63
+ if (!entries?.length)
64
+ return identity;
65
+ const entry = entries[index];
66
+ return entry && key in entry ? amount(key, entry[key]) : null;
67
+ };
68
+ const range = along(track, pick, restingAmount(resting, index, key, identity));
69
+ if (!range)
70
+ return null;
71
+ channels.push({ property: key, range });
72
+ }
73
+ return channels;
74
+ }
75
+ /** A transform group no keyframe touches, held as the element has it. */
76
+ function heldGroup(resting) {
77
+ if (!Array.isArray(resting))
78
+ return [];
79
+ const channels = [];
80
+ for (const entry of resting) {
81
+ const [key, value] = Object.entries(entry)[0] ?? [];
82
+ const number = key === undefined ? null : amount(key, value);
83
+ if (key === undefined || number === null)
84
+ return null;
85
+ channels.push({ property: key, value: number });
86
+ }
87
+ return channels;
88
+ }
89
+ function restingAmount(resting, index, key, identity) {
90
+ const entry = Array.isArray(resting) ? resting[index] : undefined;
91
+ return (entry && key in entry ? amount(key, entry[key]) : null) ?? identity;
92
+ }
93
+ const numberOr = (value, otherwise) => typeof value === 'number' ? value : otherwise;
94
+ const RADIANS = {
95
+ deg: Math.PI / 180,
96
+ rad: 1,
97
+ grad: Math.PI / 200,
98
+ turn: 2 * Math.PI,
99
+ };
100
+ /**
101
+ * A transform entry's amount as native's transform node takes it: points, a factor, or an angle
102
+ * in radians. Null for what it cannot take, a percentage translate.
103
+ */
104
+ function amount(key, value) {
105
+ if (typeof value === 'number')
106
+ return value;
107
+ if (typeof value !== 'string')
108
+ return null;
109
+ const angle = /^(-?[\d.]+)(deg|rad|grad|turn)$/.exec(value);
110
+ if (angle && (key.startsWith('rotate') || key.startsWith('skew'))) {
111
+ return Number(angle[1]) * RADIANS[angle[2]];
112
+ }
113
+ return null;
114
+ }
115
+ /**
116
+ * One property's frames laid along the scroll: each keyframe at its share of the range, eased
117
+ * segments sampled, and the resting value just past an end the animation does not fill.
118
+ */
119
+ function laid(track, pick, rest, spec, [start, end]) {
120
+ const values = track.map((point) => pick(point.value));
121
+ if (values.some((value) => value === null))
122
+ return null;
123
+ // Each point keeps its own timing function, which eases the stretch it starts.
124
+ const numeric = track.map((point, i) => ({ ...point, value: values[i] }));
125
+ const points = sampled(numeric, spec, start, Math.max(end - start, 1e-3));
126
+ const fills = spec.fill;
127
+ const backwards = fills === 'backwards' || fills === 'both';
128
+ const forwards = fills === 'forwards' || fills === 'both';
129
+ if (!backwards)
130
+ points.unshift([points[0][0] - 1e-3, rest]);
131
+ if (!forwards)
132
+ points.push([points.at(-1)[0] + 1e-3, rest]);
133
+ return { input: points.map(([x]) => x), output: points.map(([, y]) => round(y)) };
134
+ }
135
+ /** A numeric track's points along the scroll, each eased segment sampled, in offset order. */
136
+ function sampled(numeric, spec, start, span) {
137
+ const reversed = spec.direction === 'reverse' || spec.direction === 'alternate-reverse';
138
+ const points = [];
139
+ for (let i = 0; i < numeric.length - 1; i++) {
140
+ const from = numeric[i];
141
+ const to = numeric[i + 1];
142
+ const steps = isLinear(from.easing ?? spec.easing) ? 1 : SAMPLES;
143
+ for (let s = i === 0 ? 0 : 1; s <= steps; s++) {
144
+ const offset = from.offset + ((to.offset - from.offset) * s) / steps;
145
+ const value = at(numeric, offset, spec.easing);
146
+ points.push([start + (reversed ? 1 - offset : offset) * span, value]);
147
+ }
148
+ }
149
+ if (points.length === 1)
150
+ points.push([start + span, points[0][1]]);
151
+ return points.sort((a, b) => a[0] - b[0]);
152
+ }
153
+ const isLinear = (easing) => easing[0] === 0 && easing[1] === 0 && easing[2] === 1 && easing[3] === 1;
154
+ const round = (value) => Math.round(value * 1e6) / 1e6;
155
+ /** The values a scroll-driven animation commits: its frame at the offset the view starts at. */
156
+ export function firstFrame(tracks, spec, range) {
157
+ const [start, end] = range;
158
+ const reversed = spec.direction === 'reverse' || spec.direction === 'alternate-reverse';
159
+ const before = 0 < start;
160
+ if (before && spec.fill !== 'backwards' && spec.fill !== 'both')
161
+ return {};
162
+ const through = Math.min(1, Math.max(0, (0 - start) / Math.max(end - start, 1e-3)));
163
+ const local = reversed ? 1 - through : through;
164
+ const values = {};
165
+ for (const [property, track] of tracks)
166
+ values[property] = at(track, local, spec.easing);
167
+ return values;
168
+ }
169
+ //# sourceMappingURL=scroll-animation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll-animation.js","sourceRoot":"","sources":["../src/scroll-animation.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A `@keyframes` animation played by a scroll view's offset: `animation-timeline: scroll()`.\n *\n * Native interpolates linearly between the points it is given and clamps past both ends, which is\n * all a scroll-driven animation needs from it once its keyframes are laid along the offset: each\n * keyframe sits at its share of the range, a segment's timing function is sampled into points\n * along it, and an unfilled end falls back to the resting value just outside the range. What\n * native can animate this way is opacity and the transforms; anything else holds its first frame.\n */\nimport type { ScrollRange } from './native-drive.ts';\nimport { at, type AnimationSpec, type TrackPoint } from './transition.ts';\n\ntype Track = readonly { offset: number; value: unknown }[];\n\n/** The channels a scroll-driven animation drives natively, in the order native applies them. */\nexport interface DrivenChannels {\n readonly opacity?: ScrollRange;\n /** Every transform entry the view paints: driven by the scroll, or held as it is. */\n readonly transform: readonly TransformChannel[];\n}\n\nexport type TransformChannel =\n | { readonly property: string; readonly range: ScrollRange }\n | { readonly property: string; readonly value: number };\n\n/** What a range with no end, or a percentage of one, stands in for until the scroll view says. */\nconst UNKNOWN_EXTENT = 1e6;\n\n/** Points per eased segment: enough that the curve between them reads as the curve. */\nconst SAMPLES = 12;\n\n/** The transform groups in the order CSS applies them, as the engine composes them. */\nconst GROUPS = ['__translate', '__rotate', '__scale', 'transform'] as const;\n\n/** Where the range starts and ends on the scroll view's offset. */\nexport function rangeOf(spec: AnimationSpec, extent: number | null): [number, number] {\n const end = (value: number | string | undefined, otherwise: number): number => {\n if (value === undefined) return otherwise;\n if (typeof value === 'number') return value;\n return (parseFloat(value) / 100) * (extent ?? UNKNOWN_EXTENT);\n };\n return [end(spec.range?.start, 0), end(spec.range?.end, extent ?? UNKNOWN_EXTENT)];\n}\n\n/**\n * The channels for an animation's tracks over a range, and the properties it cannot drive\n * natively, which the caller holds at their first frame.\n */\nexport function scrollChannels(\n tracks: ReadonlyMap<string, Track>,\n spec: AnimationSpec,\n resting: Readonly<Record<string, unknown>>,\n range: readonly [number, number],\n): { channels: DrivenChannels; held: string[] } {\n const along = (track: Track, pick: (value: unknown) => number | null, rest: number) =>\n laid(track, pick, rest, spec, range);\n const held: string[] = [];\n let opacity: ScrollRange | undefined;\n const transform: TransformChannel[] = [];\n for (const [property, track] of tracks) {\n if (property === 'opacity') {\n opacity =\n along(\n track,\n (value) => (typeof value === 'number' ? value : null),\n numberOr(resting['opacity'], 1),\n ) ?? undefined;\n if (!opacity) held.push(property);\n } else if (!(GROUPS as readonly string[]).includes(property)) {\n held.push(property);\n }\n }\n for (const group of GROUPS) {\n const track = tracks.get(group);\n const channels = track ? drivenGroup(track, resting[group], along) : heldGroup(resting[group]);\n if (channels === null) held.push(group);\n else transform.push(...channels);\n }\n const anyDriven = transform.some((channel) => 'range' in channel);\n return {\n channels: { ...(opacity ? { opacity } : {}), transform: anyDriven ? transform : [] },\n held,\n };\n}\n\n/** One transform group's entries, each driven along the scroll; null if their shapes differ. */\nfunction drivenGroup(\n track: Track,\n resting: unknown,\n along: (\n track: Track,\n pick: (value: unknown) => number | null,\n rest: number,\n ) => ScrollRange | null,\n): TransformChannel[] | null {\n const shapes = track.map((point) => (Array.isArray(point.value) ? point.value : null));\n const shape = shapes.find((entries) => entries?.length) ?? [];\n const keys = shape.map((entry) => Object.keys(entry as object)[0]!);\n const channels: TransformChannel[] = [];\n for (const [index, key] of keys.entries()) {\n const identity = key.startsWith('scale') ? 1 : 0;\n const pick = (value: unknown) => {\n const entries = Array.isArray(value) ? value : null;\n if (!entries?.length) return identity;\n const entry = entries[index] as Record<string, unknown> | undefined;\n return entry && key in entry ? amount(key, entry[key]) : null;\n };\n const range = along(track, pick, restingAmount(resting, index, key, identity));\n if (!range) return null;\n channels.push({ property: key, range });\n }\n return channels;\n}\n\n/** A transform group no keyframe touches, held as the element has it. */\nfunction heldGroup(resting: unknown): TransformChannel[] | null {\n if (!Array.isArray(resting)) return [];\n const channels: TransformChannel[] = [];\n for (const entry of resting as Record<string, unknown>[]) {\n const [key, value] = Object.entries(entry)[0] ?? [];\n const number = key === undefined ? null : amount(key, value);\n if (key === undefined || number === null) return null;\n channels.push({ property: key, value: number });\n }\n return channels;\n}\n\nfunction restingAmount(resting: unknown, index: number, key: string, identity: number): number {\n const entry = Array.isArray(resting) ? (resting[index] as Record<string, unknown>) : undefined;\n return (entry && key in entry ? amount(key, entry[key]) : null) ?? identity;\n}\n\nconst numberOr = (value: unknown, otherwise: number) =>\n typeof value === 'number' ? value : otherwise;\n\nconst RADIANS: Record<string, number> = {\n deg: Math.PI / 180,\n rad: 1,\n grad: Math.PI / 200,\n turn: 2 * Math.PI,\n};\n\n/**\n * A transform entry's amount as native's transform node takes it: points, a factor, or an angle\n * in radians. Null for what it cannot take, a percentage translate.\n */\nfunction amount(key: string, value: unknown): number | null {\n if (typeof value === 'number') return value;\n if (typeof value !== 'string') return null;\n const angle = /^(-?[\\d.]+)(deg|rad|grad|turn)$/.exec(value);\n if (angle && (key.startsWith('rotate') || key.startsWith('skew'))) {\n return Number(angle[1]) * RADIANS[angle[2]!]!;\n }\n return null;\n}\n\n/**\n * One property's frames laid along the scroll: each keyframe at its share of the range, eased\n * segments sampled, and the resting value just past an end the animation does not fill.\n */\nfunction laid(\n track: Track,\n pick: (value: unknown) => number | null,\n rest: number,\n spec: AnimationSpec,\n [start, end]: readonly [number, number],\n): ScrollRange | null {\n const values = track.map((point) => pick(point.value));\n if (values.some((value) => value === null)) return null;\n // Each point keeps its own timing function, which eases the stretch it starts.\n const numeric = track.map((point, i) => ({ ...point, value: values[i]! }));\n const points = sampled(numeric, spec, start, Math.max(end - start, 1e-3));\n const fills = spec.fill;\n const backwards = fills === 'backwards' || fills === 'both';\n const forwards = fills === 'forwards' || fills === 'both';\n if (!backwards) points.unshift([points[0]![0] - 1e-3, rest]);\n if (!forwards) points.push([points.at(-1)![0] + 1e-3, rest]);\n return { input: points.map(([x]) => x), output: points.map(([, y]) => round(y)) };\n}\n\n/** A numeric track's points along the scroll, each eased segment sampled, in offset order. */\nfunction sampled(\n numeric: readonly (TrackPoint & { value: number })[],\n spec: AnimationSpec,\n start: number,\n span: number,\n): [number, number][] {\n const reversed = spec.direction === 'reverse' || spec.direction === 'alternate-reverse';\n const points: [number, number][] = [];\n for (let i = 0; i < numeric.length - 1; i++) {\n const from = numeric[i]!;\n const to = numeric[i + 1]!;\n const steps = isLinear(from.easing ?? spec.easing) ? 1 : SAMPLES;\n for (let s = i === 0 ? 0 : 1; s <= steps; s++) {\n const offset = from.offset + ((to.offset - from.offset) * s) / steps;\n const value = at(numeric, offset, spec.easing) as number;\n points.push([start + (reversed ? 1 - offset : offset) * span, value]);\n }\n }\n if (points.length === 1) points.push([start + span, points[0]![1]]);\n return points.sort((a, b) => a[0] - b[0]);\n}\n\nconst isLinear = (easing: readonly number[]) =>\n easing[0] === 0 && easing[1] === 0 && easing[2] === 1 && easing[3] === 1;\n\nconst round = (value: number) => Math.round(value * 1e6) / 1e6;\n\n/** The values a scroll-driven animation commits: its frame at the offset the view starts at. */\nexport function firstFrame(\n tracks: ReadonlyMap<string, Track>,\n spec: AnimationSpec,\n range: readonly [number, number],\n): Record<string, unknown> {\n const [start, end] = range;\n const reversed = spec.direction === 'reverse' || spec.direction === 'alternate-reverse';\n const before = 0 < start;\n if (before && spec.fill !== 'backwards' && spec.fill !== 'both') return {};\n const through = Math.min(1, Math.max(0, (0 - start) / Math.max(end - start, 1e-3)));\n const local = reversed ? 1 - through : through;\n const values: Record<string, unknown> = {};\n for (const [property, track] of tracks) values[property] = at(track, local, spec.easing);\n return values;\n}\n"]}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * CSS transitions, the runtime half.
3
+ *
4
+ * React Native has no CSS animation of any kind, so a `transition` in a stylesheet is compiled to
5
+ * a spec and driven here: the engine notices a transitioning property change between commits,
6
+ * holds the old value, and interpolates towards the new one as the clock advances.
7
+ *
8
+ * This runs in JavaScript, unlike `AnimatedStyle`, which can hand a whole animation to the native
9
+ * driver. It has to: a transition is a reaction to the cascade recomputing, and only this side
10
+ * knows a value changed at all. The cost is a commit per frame, which is the same cost a
11
+ * JavaScript-driven `Animated` value has.
12
+ */
13
+ /** What the CSS compiler emits per transitioning property. Durations are in milliseconds. */
14
+ export interface TransitionSpec {
15
+ readonly duration: number;
16
+ readonly delay: number;
17
+ /** The four control points of the timing curve; a keyword is resolved at build time. */
18
+ readonly easing: readonly number[];
19
+ }
20
+ export interface Transition {
21
+ from: unknown;
22
+ to: unknown;
23
+ /** When the interpolation starts, delay already added. */
24
+ start: number;
25
+ duration: number;
26
+ easing: readonly number[];
27
+ /** What the node paints right now. Also the value a redirected transition starts from. */
28
+ current: unknown;
29
+ done: boolean;
30
+ }
31
+ export declare function bezier(points: readonly number[], x: number): number;
32
+ /** A colour as `[r, g, b, a]`, or null for a form not worth parsing. */
33
+ export declare function parseColor(value: unknown): [number, number, number, number] | null;
34
+ /** Whether a word is a CSS colour name, such as `red` or `transparent`. */
35
+ export declare function isNamedColor(value: string): boolean;
36
+ /** One operation's value: a number, or a number keeping whatever unit it was written in. */
37
+ export declare function tween(from: unknown, to: unknown, t: number): unknown;
38
+ /**
39
+ * One step between two values.
40
+ *
41
+ * Numbers, colours and transforms interpolate. Everything else steps to the new value at the end,
42
+ * which is what CSS does for a property it calls discrete, and is right for the keywords and
43
+ * strings that make up most of the rest.
44
+ */
45
+ export declare function interpolate(from: unknown, to: unknown, t: number): unknown;
46
+ /**
47
+ * Bring one property up to date, and say whether that started a transition.
48
+ *
49
+ * Mutates `props[key]` to what should be painted now, which for a transition in flight is the
50
+ * value it has reached rather than the one it is heading for.
51
+ */
52
+ export declare function step(state: Map<string, Transition>, key: string, props: Record<string, unknown>, rule: TransitionSpec | undefined, now: number): boolean;
53
+ /** A property seen for the first time: recorded so the next change has something to aim from. */
54
+ export declare function settled(value: unknown, rule: TransitionSpec): Transition;
55
+ /** Aim an existing transition at a new value, starting from wherever it had got to. */
56
+ export declare function redirect(transition: Transition, target: unknown, rule: TransitionSpec, now: number): void;
57
+ /** What the CSS compiler emits for `animation:`. Times are milliseconds. */
58
+ export interface AnimationSpec {
59
+ readonly name: string;
60
+ readonly duration: number;
61
+ readonly delay: number;
62
+ readonly easing: readonly number[];
63
+ /** `Infinity` for `infinite`. */
64
+ /** `null` is `infinite`, which is what survives being written into the bundle as JSON. */
65
+ readonly iterations: number | null;
66
+ readonly fill: 'none' | 'forwards' | 'backwards' | 'both';
67
+ /** Which way each iteration plays; absent is `normal`, forwards every time. */
68
+ readonly direction?: 'reverse' | 'alternate' | 'alternate-reverse';
69
+ /**
70
+ * `animation-timeline: scroll()`: played by the nearest scroll view's offset along this axis
71
+ * rather than by the clock. See `scroll-animation.ts`.
72
+ */
73
+ readonly timeline?: 'x' | 'y';
74
+ /** `animation-play-state: paused`: held at the frame it has reached, until it runs again. */
75
+ readonly paused?: boolean;
76
+ /**
77
+ * Where along the scroll it plays, in points or as a percentage of how far the view scrolls.
78
+ * An end left out is the scroll's own: 0, or all the way.
79
+ */
80
+ readonly range?: {
81
+ readonly start?: number | string;
82
+ readonly end?: number | string;
83
+ };
84
+ }
85
+ /** One `@keyframes` frame: where it sits, and what it sets there. */
86
+ export interface Keyframe {
87
+ readonly offset: number;
88
+ readonly declarations: Record<string, unknown>;
89
+ /** The `animation-timing-function` written in this keyframe, easing it to the next. */
90
+ readonly easing?: readonly number[];
91
+ }
92
+ export interface RunningAnimation {
93
+ spec: AnimationSpec;
94
+ /** When it was paused, while it is; the clock it is sampled at stands still there. */
95
+ pausedAt?: number;
96
+ /** Per property, the offsets that mention it, in order. Built once when the animation starts. */
97
+ readonly tracks: Map<string, TrackPoint[]>;
98
+ start: number;
99
+ /** What the properties read right now. Empty once a finished animation stops filling. */
100
+ values: Record<string, unknown>;
101
+ done: boolean;
102
+ }
103
+ /**
104
+ * Build one track per property the frames touch.
105
+ *
106
+ * A property that no frame sets at 0% or 100% is anchored to the value the element would
107
+ * otherwise have, which is what CSS means by an implicit keyframe: `to { opacity: 1 }` alone
108
+ * animates from wherever the element already was.
109
+ */
110
+ export declare function tracksOf(frames: readonly Keyframe[], resting: Record<string, unknown>): Map<string, TrackPoint[]>;
111
+ /**
112
+ * Where an animation has got to, as the properties to paint.
113
+ *
114
+ * The easing runs per segment rather than across the whole pass, which is what CSS does with a
115
+ * single timing function: each pair of frames is eased on its own.
116
+ */
117
+ export declare function sample(running: RunningAnimation, now: number): {
118
+ values: Record<string, unknown>;
119
+ finished: boolean;
120
+ };
121
+ /**
122
+ * One property's value at one keyframe, and the curve a keyframe of its own eases it on to the
123
+ * next one by, where it has one.
124
+ */
125
+ export interface TrackPoint {
126
+ readonly offset: number;
127
+ readonly value: unknown;
128
+ readonly easing?: readonly number[];
129
+ }
130
+ export declare function at(track: readonly TrackPoint[], local: number, easing: readonly number[]): unknown;
131
+ /**
132
+ * The event object Angular reads.
133
+ *
134
+ * A keyframe animation is built with the global `AnimationEvent` when the host has defined one,
135
+ * because Angular tells an animation from a transition with `event instanceof AnimationEvent` and
136
+ * picks which end event to wait for from the answer. A transition is a plain object for the same
137
+ * reason: it must not be one.
138
+ */
139
+ export declare function animationEvent(topLevelType: string, target: unknown, name: string): unknown;