@flemo/core 2.2.1 → 2.2.2
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/dist/core/engine/__tests__/createTransitionEngine.cameraSpan.test.d.ts +1 -0
- package/dist/dom/attributes.d.ts +16 -1
- package/dist/dom/staging.d.ts +10 -1
- package/dist/index.mjs +1848 -1625
- package/dist/morph/__tests__/morphContents.test.d.ts +1 -0
- package/dist/morph/__tests__/pinParts.test.d.ts +1 -0
- package/dist/morph/attachMorph.d.ts +14 -0
- package/dist/morph/morphContents.d.ts +36 -0
- package/dist/morph/morphFace.d.ts +5 -0
- package/dist/morph/morphKeyframes.d.ts +105 -0
- package/dist/morph/morphLine.d.ts +33 -10
- package/dist/morph/morphPose.d.ts +18 -1
- package/dist/morph/morphSide.d.ts +1 -8
- package/dist/morph/pinParts.d.ts +27 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -8,6 +8,18 @@ export interface AttachMorphOptions {
|
|
|
8
8
|
name?: MorphTransitionName;
|
|
9
9
|
/** The navigate store of the Router scope this element belongs to. */
|
|
10
10
|
navigateStore: NavigateStoreApi;
|
|
11
|
+
/**
|
|
12
|
+
* Which side of which flight this end is on, from the binding.
|
|
13
|
+
*
|
|
14
|
+
* Only ever needed where the DOM cannot answer: a shared bar is rendered
|
|
15
|
+
* outside the screen scope it belongs to, so walking up from a morph in one
|
|
16
|
+
* leaves the screen entirely or lands on an enclosing Router's. Everything
|
|
17
|
+
* written inside a screen is answered by the walk and needs none of this.
|
|
18
|
+
*/
|
|
19
|
+
ownership?: {
|
|
20
|
+
status: NavigateStatus;
|
|
21
|
+
active: boolean;
|
|
22
|
+
} | null;
|
|
11
23
|
}
|
|
12
24
|
interface MorphFlight {
|
|
13
25
|
finish: () => void;
|
|
@@ -15,6 +27,8 @@ interface MorphFlight {
|
|
|
15
27
|
element: HTMLElement;
|
|
16
28
|
duration: number;
|
|
17
29
|
start: number;
|
|
30
|
+
/** The flat lead-in inside `start`, so a nested set bakes the same one. */
|
|
31
|
+
head: number;
|
|
18
32
|
ease: AnimationOptions["ease"];
|
|
19
33
|
/**
|
|
20
34
|
* Put the landing's safety net away, and set it again.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WHETHER A BOX'S CONTENTS MOVE WHEN THE BOX CHANGES SIZE.
|
|
3
|
+
*
|
|
4
|
+
* Animating a morph's box is what makes it a growth rather than a stretch: the
|
|
5
|
+
* subtree lays itself out at every size on the way, which is the only way text
|
|
6
|
+
* can re-wrap into its new shape. That is a layout and a fresh raster of the
|
|
7
|
+
* subtree on every frame, and WebKit re-snaps the backing to the device grid
|
|
8
|
+
* each time, so a subtree that does not need to move gets carried a device
|
|
9
|
+
* pixel back and forth for the whole flight anyway.
|
|
10
|
+
*
|
|
11
|
+
* Measured on a consumer's pill: every descendant held ONE position for all
|
|
12
|
+
* twenty-three frames of the flight, and only the box's own near edge moved.
|
|
13
|
+
* Right-aligned contents in a box that grows leftward do not go anywhere. The
|
|
14
|
+
* per-frame layout produced no layout change at all, and the tremble was the
|
|
15
|
+
* whole of what it bought.
|
|
16
|
+
*
|
|
17
|
+
* Where that is TRUE, holding the box at the size that contains both ends and
|
|
18
|
+
* cutting the near edge back with a clip is the same picture, drawn once. Where
|
|
19
|
+
* it is FALSE the flight must animate the box for real, because something
|
|
20
|
+
* inside genuinely has a different place at the two ends.
|
|
21
|
+
*
|
|
22
|
+
* This is the difference between measuring that and guessing it from the box's
|
|
23
|
+
* shape. A shape says nothing about a consumer's subtree; two laid-out ends do.
|
|
24
|
+
*/
|
|
25
|
+
/** The corner a flight's box is anchored on, and grows away from. */
|
|
26
|
+
export interface MorphAnchor {
|
|
27
|
+
x: "left" | "right";
|
|
28
|
+
y: "top" | "bottom";
|
|
29
|
+
}
|
|
30
|
+
export declare const contentsHoldAcrossBox: (element: HTMLElement, from: {
|
|
31
|
+
width: number;
|
|
32
|
+
height: number;
|
|
33
|
+
}, to: {
|
|
34
|
+
width: number;
|
|
35
|
+
height: number;
|
|
36
|
+
}, anchor: MorphAnchor) => boolean;
|
|
@@ -41,6 +41,11 @@ export declare const faceParts: (size: number, font: {
|
|
|
41
41
|
weight: string | number;
|
|
42
42
|
style: string;
|
|
43
43
|
}, scale: number) => FaceParts | null;
|
|
44
|
+
export declare const runAdvance: (text: string, size: number, font: {
|
|
45
|
+
family: string;
|
|
46
|
+
weight: string | number;
|
|
47
|
+
style: string;
|
|
48
|
+
}) => number | null;
|
|
44
49
|
/** The face's own height at one size: its two halves added up. */
|
|
45
50
|
export declare const faceHeight: (size: number, font: {
|
|
46
51
|
family: string;
|
|
@@ -19,6 +19,24 @@ export interface MorphTravel {
|
|
|
19
19
|
duration: number;
|
|
20
20
|
/** Seconds from the release, platform head included. */
|
|
21
21
|
start: number;
|
|
22
|
+
/**
|
|
23
|
+
* The platform's flat lead-in, ALREADY COUNTED IN `start`.
|
|
24
|
+
*
|
|
25
|
+
* A HEAD IS NOT A DELAY. Waiting it out as a delay leaves the animation
|
|
26
|
+
* uncommitted until the instant it must move, so a first frame that arrives
|
|
27
|
+
* late arrives PARTWAY THROUGH — the curve is entered wherever the clock
|
|
28
|
+
* says and everything before that is never drawn. Given here, the same
|
|
29
|
+
* seconds are baked into the keyframes as a flat stop instead: the animation
|
|
30
|
+
* is running and still, a late first frame lands inside the lead-in, and the
|
|
31
|
+
* curve plays from 0. The screens have ridden it this way since the head was
|
|
32
|
+
* invented; this is the flight riding it the same way.
|
|
33
|
+
*
|
|
34
|
+
* Painted frames off a consumer's phone, 60fps: the first frame the box was
|
|
35
|
+
* drawn on was already 67% of the way through its travel. The computed value
|
|
36
|
+
* ramped correctly throughout, which is why every main-thread probe called it
|
|
37
|
+
* healthy.
|
|
38
|
+
*/
|
|
39
|
+
head?: number;
|
|
22
40
|
ease: AnimationOptions["ease"];
|
|
23
41
|
}
|
|
24
42
|
export interface MorphKeyframeSet {
|
|
@@ -47,6 +65,30 @@ export interface MorphKeyframeSet {
|
|
|
47
65
|
* is what keeps its position on the same thread as its size.
|
|
48
66
|
*/
|
|
49
67
|
translate: string | null;
|
|
68
|
+
/**
|
|
69
|
+
* The `width` and `height` the caller must set on the element, or null.
|
|
70
|
+
*
|
|
71
|
+
* Non-null where the box's size is driven through registered properties,
|
|
72
|
+
* which is what keeps an engine from dropping it (see morphPose).
|
|
73
|
+
*/
|
|
74
|
+
size: {
|
|
75
|
+
width: string;
|
|
76
|
+
height: string;
|
|
77
|
+
} | null;
|
|
78
|
+
/**
|
|
79
|
+
* The width the box is HELD at for the whole flight, or null.
|
|
80
|
+
*
|
|
81
|
+
* The viewport x of a far edge both ends agree on, when there is one.
|
|
82
|
+
*
|
|
83
|
+
* The element is placed FROM it — `left: calc(<edge>px - var(--flemo-box-w))`
|
|
84
|
+
* — so the edge is derived from the very channel the width animates on and
|
|
85
|
+
* the two round together. Reached as position + size instead, the engine
|
|
86
|
+
* rounds each to its own layout unit and their sum oscillates: measured on a
|
|
87
|
+
* consumer's pill, 366.000 with the anchor against 365.985 ± 0.015 without,
|
|
88
|
+
* reversing six times in twenty-three frames, and every right-aligned thing
|
|
89
|
+
* inside it followed.
|
|
90
|
+
*/
|
|
91
|
+
heldEdge: number | null;
|
|
50
92
|
/**
|
|
51
93
|
* The `transform` the caller must set on the element, or null.
|
|
52
94
|
*
|
|
@@ -55,6 +97,13 @@ export interface MorphKeyframeSet {
|
|
|
55
97
|
* transform that reads them (see PINNED_POSE_TRANSFORM).
|
|
56
98
|
*/
|
|
57
99
|
transform: string | null;
|
|
100
|
+
/**
|
|
101
|
+
* The `letter-spacing` the caller must set on the element, or null.
|
|
102
|
+
*
|
|
103
|
+
* Non-null where the tracking carries a correction, which is written as a sum
|
|
104
|
+
* of the author's own and the correction's, each on its own clock.
|
|
105
|
+
*/
|
|
106
|
+
letterSpacing: string | null;
|
|
58
107
|
}
|
|
59
108
|
/**
|
|
60
109
|
* The keyframes and the `animation` shorthand for one side of a morph.
|
|
@@ -88,6 +137,17 @@ export declare const buildMorphKeyframes: (input: {
|
|
|
88
137
|
from: MorphRect;
|
|
89
138
|
to: MorphRect;
|
|
90
139
|
} | null;
|
|
140
|
+
/**
|
|
141
|
+
* Whether the two ends lay their CONTENTS out in the same places, measured.
|
|
142
|
+
*
|
|
143
|
+
* A box animates for real wherever this is false, because something inside
|
|
144
|
+
* has a different place at the two ends and only a layout per frame can take
|
|
145
|
+
* it there. Where it is true the subtree is the same picture at every size on
|
|
146
|
+
* the way, so the box is laid out ONCE and the near edge is cut back with a
|
|
147
|
+
* clip instead (see the reveal below). It is measured rather than inferred
|
|
148
|
+
* from the box's shape: a shape says nothing about a consumer's subtree.
|
|
149
|
+
*/
|
|
150
|
+
contentsHold?: boolean;
|
|
91
151
|
/** Type morphs by growing, not by being scaled: px at each end. */
|
|
92
152
|
fontSize?: {
|
|
93
153
|
from: number;
|
|
@@ -124,6 +184,29 @@ export declare const buildMorphKeyframes: (input: {
|
|
|
124
184
|
at: number;
|
|
125
185
|
ascent: number;
|
|
126
186
|
}[] | null;
|
|
187
|
+
/**
|
|
188
|
+
* What the baseline owes at the START, in px, because the staircase holds the
|
|
189
|
+
* ARRIVAL's leading from the first frame (see attachMorph).
|
|
190
|
+
*
|
|
191
|
+
* Paid on the same channel as the ascent's cancellation and with the same
|
|
192
|
+
* shape: the whole amount at the departure, nothing at the landing, so what
|
|
193
|
+
* the flight travels is unchanged and only its first frame moves.
|
|
194
|
+
*/
|
|
195
|
+
leadStart?: number | null;
|
|
196
|
+
/**
|
|
197
|
+
* The correction that keeps a growing run of glyphs from drifting apart.
|
|
198
|
+
*
|
|
199
|
+
* A run's width against its size is one curve, and where it leaves the
|
|
200
|
+
* straight line between its ends every glyph carries the error that piled up
|
|
201
|
+
* before it. Spread the negative of that over the gaps and it cancels (see
|
|
202
|
+
* morphLine). It rides `letter-spacing` beside the author's own tracking, on
|
|
203
|
+
* its own clock, and is RAMPED rather than held because what it cancels is a
|
|
204
|
+
* curve rather than a staircase.
|
|
205
|
+
*/
|
|
206
|
+
track?: {
|
|
207
|
+
at: number;
|
|
208
|
+
fix: number;
|
|
209
|
+
}[] | null;
|
|
127
210
|
/** Type's other two dimensions, so it re-typesets rather than merely re-sizing. */
|
|
128
211
|
fontWeight?: {
|
|
129
212
|
from: number;
|
|
@@ -186,12 +269,32 @@ export declare const buildMorphKeyframes: (input: {
|
|
|
186
269
|
from: MorphClipInset;
|
|
187
270
|
to: MorphClipInset;
|
|
188
271
|
} | null;
|
|
272
|
+
/**
|
|
273
|
+
* The corner the box wears, so a clip that reveals it cuts a rounded shape
|
|
274
|
+
* rather than a square one (see the reveal below).
|
|
275
|
+
*/
|
|
276
|
+
radius?: string | null;
|
|
189
277
|
fade: {
|
|
190
278
|
from: TransitionTarget | null;
|
|
191
279
|
to: TransitionTarget | null;
|
|
192
280
|
duration: number;
|
|
193
281
|
/** Seconds to hold the from-pose before the fade runs, on top of `travel.start`. */
|
|
194
282
|
delay?: number;
|
|
283
|
+
/**
|
|
284
|
+
* The curve, where the flight's own is not the right one.
|
|
285
|
+
*
|
|
286
|
+
* A HAND-OVER IS A STEP, NOT A RAMP. Two opacity ramps crossing never
|
|
287
|
+
* compose back to what they replaced: at the midpoint of a 1-to-0 against a
|
|
288
|
+
* 0-to-1, alpha compositing leaves 1 - (1 - 0.5) * 0.5 = 0.75 of the pair,
|
|
289
|
+
* and the engines do not even sample the two on the same phase. Device-read
|
|
290
|
+
* on a consumer's tab switch: the copy at 0.48 against the arrival at 0.33,
|
|
291
|
+
* two frames of a washed-out box mid-travel — read as a blink.
|
|
292
|
+
*
|
|
293
|
+
* A step at the same instant on both sides has no such midpoint. Both are
|
|
294
|
+
* pure functions of one timeline, so every frame that renders at all
|
|
295
|
+
* renders exactly one of them, and a missed frame cannot land between.
|
|
296
|
+
*/
|
|
297
|
+
easing?: string;
|
|
195
298
|
} | null;
|
|
196
299
|
/**
|
|
197
300
|
* Everything the two ends paint differently — corner, surface, border,
|
|
@@ -291,6 +394,8 @@ export declare const buildCameraKeyframes: (input: {
|
|
|
291
394
|
settling: boolean;
|
|
292
395
|
duration: number;
|
|
293
396
|
start: number;
|
|
397
|
+
/** The flight's flat lead-in, baked here too: the camera is one of its parts. */
|
|
398
|
+
head?: number;
|
|
294
399
|
ease: AnimationOptions["ease"];
|
|
295
400
|
selector: string;
|
|
296
401
|
/**
|
|
@@ -28,6 +28,23 @@ export interface LeadingEnd {
|
|
|
28
28
|
/** What the engine actually rendered for this end (see MorphSnapshot). */
|
|
29
29
|
leadOffset: number | null;
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* The half-leading a flight owes at its START, in px.
|
|
33
|
+
*
|
|
34
|
+
* The staircase holds ONE leading for the whole flight so the rendered
|
|
35
|
+
* half-leading cannot step, and the one it holds is the ARRIVAL's, because that
|
|
36
|
+
* is the value the landing has to restore. At the other end that makes the
|
|
37
|
+
* first frame render a line-height the departure never had, and half of that
|
|
38
|
+
* difference is where the departure's baseline sat.
|
|
39
|
+
*
|
|
40
|
+
* Half of the difference is not the answer, though: the engine puts the
|
|
41
|
+
* half-leading on a grid, so a line-height a pixel apart can render a whole
|
|
42
|
+
* pixel apart rather than half of one. Both ends reported where their line was
|
|
43
|
+
* actually put, so the grid is the one that reproduces both — the same
|
|
44
|
+
* authority `leadingBias` answers to — and where no candidate does, the
|
|
45
|
+
* arithmetic difference is the honest fallback.
|
|
46
|
+
*/
|
|
47
|
+
export declare const leadingOwed: (from: LeadingEnd, to: LeadingEnd, stops: LeadingStop[] | null) => number;
|
|
31
48
|
export declare const leadingBias: (from: LeadingEnd, to: LeadingEnd) => number;
|
|
32
49
|
export default holdOneLine;
|
|
33
50
|
export interface LeadingEndType {
|
|
@@ -56,18 +73,24 @@ export interface LeadingStop {
|
|
|
56
73
|
*/
|
|
57
74
|
ascent: number;
|
|
58
75
|
}
|
|
59
|
-
/**
|
|
60
|
-
* The line-height stops that hold a type morph's leading still, or null.
|
|
61
|
-
*
|
|
62
|
-
* Null wherever the correction cannot be justified: a face whose metrics cannot
|
|
63
|
-
* be read, an end that was never measured, a flight whose type does not change
|
|
64
|
-
* size, and — the one that matters — a prediction that does not reproduce what
|
|
65
|
-
* the engine actually reported at BOTH ends. That last is what stands in for a
|
|
66
|
-
* browser check: an engine that does not quantise its face heights fails it at
|
|
67
|
-
* every size, and is left alone.
|
|
68
|
-
*/
|
|
69
76
|
export declare const leadingStops: (from: LeadingEndType, to: LeadingEndType, font: {
|
|
70
77
|
family: string;
|
|
71
78
|
weight: string | number;
|
|
72
79
|
style: string;
|
|
73
80
|
} | null, ease: AnimationOptions["ease"]) => LeadingStop[] | null;
|
|
81
|
+
export interface TrackStop {
|
|
82
|
+
/** Percent of the flight, 0 to 100. */
|
|
83
|
+
at: number;
|
|
84
|
+
/** The correction, in px, to add to every gap between the glyphs. */
|
|
85
|
+
fix: number;
|
|
86
|
+
}
|
|
87
|
+
/** The tracking correction for a growing run, or null where there is none to make. */
|
|
88
|
+
export declare const trackStops: (text: string, from: {
|
|
89
|
+
fontSize: number | null;
|
|
90
|
+
}, to: {
|
|
91
|
+
fontSize: number | null;
|
|
92
|
+
}, font: {
|
|
93
|
+
family: string;
|
|
94
|
+
weight: string | number;
|
|
95
|
+
style: string;
|
|
96
|
+
} | null, ease: AnimationOptions["ease"]) => TrackStop[] | null;
|
|
@@ -42,10 +42,27 @@ export declare const poseToCss: (pose: MorphPose) => string;
|
|
|
42
42
|
*/
|
|
43
43
|
export declare const composePoses: (poses: MorphPose[]) => MorphPose[];
|
|
44
44
|
export declare const composePosesToCss: (poses: MorphPose[]) => string;
|
|
45
|
-
/** The
|
|
45
|
+
/** The size an element wears while its box is driven through the channel. */
|
|
46
|
+
/** The channel a travelling box carries its width on, for anything placed from it. */
|
|
47
|
+
export declare const BOX_WIDTH_PROPERTY: "--flemo-box-w";
|
|
48
|
+
export declare const PINNED_BOX: string;
|
|
49
|
+
export declare const PINNED_BOX_HEIGHT: string;
|
|
50
|
+
/** One end of a pinned box, as the keyframe declarations that drive it. */
|
|
51
|
+
export declare const pinnedBoxDecls: (width: number, height: number, indent?: string) => string;
|
|
52
|
+
export declare const RULER: number;
|
|
53
|
+
export declare const onRuler: (value: number) => number;
|
|
54
|
+
export declare const pinnedBoxDeclsOnRuler: (width: number, height: number, indent?: string) => string;
|
|
55
|
+
/** The `letter-spacing` an element wears while its tracking is corrected. */
|
|
56
|
+
export declare const PINNED_TRACK: string;
|
|
57
|
+
/** One end of the authored tracking. */
|
|
58
|
+
export declare const pinnedTrackDecl: (value: number, indent?: string) => string;
|
|
59
|
+
/** One stop of the correction that keeps a run from drifting apart. */
|
|
60
|
+
export declare const pinnedTrackFixDecl: (value: number, indent?: string) => string;
|
|
46
61
|
export declare const PINNED_TRAVEL: string;
|
|
47
62
|
/** One end of a pinned travel, as the keyframe declarations that drive it. */
|
|
48
63
|
export declare const pinnedTravelDecls: (x: number, y: number, indent?: string) => string;
|
|
64
|
+
/** One stop of the ascent's staircase, on the half of the pair that holds. */
|
|
65
|
+
export declare const pinnedLiftDecl: (ascent: number, indent?: string) => string;
|
|
49
66
|
/** The `transform` an element wears while its pose is pinned. */
|
|
50
67
|
export declare const PINNED_POSE_TRANSFORM: string;
|
|
51
68
|
/**
|
|
@@ -39,11 +39,4 @@ export interface MorphSide {
|
|
|
39
39
|
screenDuration: number;
|
|
40
40
|
screenEase: AnimationOptions["ease"];
|
|
41
41
|
}
|
|
42
|
-
|
|
43
|
-
* Where one side of a morph really is, and how long its screen takes.
|
|
44
|
-
*
|
|
45
|
-
* The screen is read from the DOM PROTOCOL — its transition name is an
|
|
46
|
-
* attribute — rather than from a store, so a morph works the same for any
|
|
47
|
-
* binding and needs nothing threaded through from the consumer's tree.
|
|
48
|
-
*/
|
|
49
|
-
export declare const resolveMorphSide: (element: HTMLElement, screen: HTMLElement, variant: TransitionVariant) => MorphSide;
|
|
42
|
+
export declare const resolveMorphSide: (element: HTMLElement, owner: HTMLElement | null, variant: TransitionVariant) => MorphSide;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A PART IS NOT LAID OUT AT THE SIZES THE BOX PASSES THROUGH.
|
|
3
|
+
*
|
|
4
|
+
* A morph's box animates so that the subtree can lay itself out at every size
|
|
5
|
+
* on the way, which is what makes the artwork and the paired type GROW. A
|
|
6
|
+
* `<Part>` is the opposite declaration: it is the content that is NOT paired,
|
|
7
|
+
* that the flight hides and brings back on its own clock, precisely because it
|
|
8
|
+
* has no business being laid out at a cell's width.
|
|
9
|
+
*
|
|
10
|
+
* It was laid out there anyway. The part's own box still rode the growing box,
|
|
11
|
+
* so a page of copy inside a card re-wrapped the whole way up, and the part is
|
|
12
|
+
* brought back before the box has finished growing. Measured on the reference
|
|
13
|
+
* detail, frame by frame: the body copy stood at 147.88px tall for the first
|
|
14
|
+
* twenty frames of the flight, dropped a line to 126.75px on the twenty-first,
|
|
15
|
+
* and the facts list and the buy button under it jumped 23.55px up the screen
|
|
16
|
+
* in that one frame, with the copy already at full opacity.
|
|
17
|
+
*
|
|
18
|
+
* So a part is laid out ONCE, at the width it will rest at, and the box's
|
|
19
|
+
* growth is a clip over it rather than a re-wrap of it. Nothing is lost: the
|
|
20
|
+
* part is not on glass at those sizes, and where it is on glass the width is
|
|
21
|
+
* the one it lands at.
|
|
22
|
+
*
|
|
23
|
+
* The width alone. A part's HEIGHT is where the growth actually shows through
|
|
24
|
+
* to the parts below it, and holding that would pin a subtree to a height its
|
|
25
|
+
* own copy no longer needs.
|
|
26
|
+
*/
|
|
27
|
+
export declare const pinPartWidths: (element: HTMLElement) => (() => void);
|
package/package.json
CHANGED