react-native-screen-transitions 3.6.0-alpha.4 → 3.6.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.
- package/lib/commonjs/shared/providers/screen/styles/helpers/resolve-slot-styles/index.js +94 -13
- package/lib/commonjs/shared/providers/screen/styles/helpers/resolve-slot-styles/index.js.map +1 -1
- package/lib/commonjs/shared/providers/screen/styles/helpers/resolve-slot-styles/materialize-slot.js +4 -1
- package/lib/commonjs/shared/providers/screen/styles/helpers/resolve-slot-styles/materialize-slot.js.map +1 -1
- package/lib/commonjs/shared/providers/screen/styles/helpers/resolve-slot-styles/types.js +4 -0
- package/lib/commonjs/shared/providers/screen/styles/hooks/use-interpolated-style-maps.js +85 -28
- package/lib/commonjs/shared/providers/screen/styles/hooks/use-interpolated-style-maps.js.map +1 -1
- package/lib/commonjs/shared/providers/screen/styles/hooks/use-resolved-slot-style-map.js +5 -4
- package/lib/commonjs/shared/providers/screen/styles/hooks/use-resolved-slot-style-map.js.map +1 -1
- package/lib/commonjs/shared/providers/screen/styles/styles.provider.js +2 -2
- package/lib/commonjs/shared/providers/screen/styles/styles.provider.js.map +1 -1
- package/lib/commonjs/shared/stores/bounds/internals/links.js +18 -3
- package/lib/commonjs/shared/stores/bounds/internals/links.js.map +1 -1
- package/lib/commonjs/shared/utils/bounds/navigation/zoom/build.js +52 -52
- package/lib/commonjs/shared/utils/bounds/navigation/zoom/build.js.map +1 -1
- package/lib/commonjs/shared/utils/bounds/navigation/zoom/config.js +1 -1
- package/lib/module/shared/providers/screen/styles/helpers/resolve-slot-styles/index.js +95 -14
- package/lib/module/shared/providers/screen/styles/helpers/resolve-slot-styles/index.js.map +1 -1
- package/lib/module/shared/providers/screen/styles/helpers/resolve-slot-styles/materialize-slot.js +4 -1
- package/lib/module/shared/providers/screen/styles/helpers/resolve-slot-styles/materialize-slot.js.map +1 -1
- package/lib/module/shared/providers/screen/styles/helpers/resolve-slot-styles/types.js +2 -0
- package/lib/module/shared/providers/screen/styles/hooks/use-interpolated-style-maps.js +84 -28
- package/lib/module/shared/providers/screen/styles/hooks/use-interpolated-style-maps.js.map +1 -1
- package/lib/module/shared/providers/screen/styles/hooks/use-resolved-slot-style-map.js +5 -4
- package/lib/module/shared/providers/screen/styles/hooks/use-resolved-slot-style-map.js.map +1 -1
- package/lib/module/shared/providers/screen/styles/styles.provider.js +2 -2
- package/lib/module/shared/providers/screen/styles/styles.provider.js.map +1 -1
- package/lib/module/shared/stores/bounds/internals/links.js +18 -3
- package/lib/module/shared/stores/bounds/internals/links.js.map +1 -1
- package/lib/module/shared/utils/bounds/navigation/zoom/build.js +50 -50
- package/lib/module/shared/utils/bounds/navigation/zoom/build.js.map +1 -1
- package/lib/module/shared/utils/bounds/navigation/zoom/config.js +1 -1
- package/lib/typescript/shared/providers/screen/styles/helpers/resolve-slot-styles/index.d.ts +4 -4
- package/lib/typescript/shared/providers/screen/styles/helpers/resolve-slot-styles/index.d.ts.map +1 -1
- package/lib/typescript/shared/providers/screen/styles/helpers/resolve-slot-styles/materialize-slot.d.ts.map +1 -1
- package/lib/typescript/shared/providers/screen/styles/helpers/resolve-slot-styles/types.d.ts +2 -0
- package/lib/typescript/shared/providers/screen/styles/helpers/resolve-slot-styles/types.d.ts.map +1 -1
- package/lib/typescript/shared/providers/screen/styles/hooks/use-interpolated-style-maps.d.ts +6 -6
- package/lib/typescript/shared/providers/screen/styles/hooks/use-interpolated-style-maps.d.ts.map +1 -1
- package/lib/typescript/shared/providers/screen/styles/hooks/use-resolved-slot-style-map.d.ts +3 -2
- package/lib/typescript/shared/providers/screen/styles/hooks/use-resolved-slot-style-map.d.ts.map +1 -1
- package/lib/typescript/shared/stores/bounds/internals/links.d.ts.map +1 -1
- package/lib/typescript/shared/types/bounds.types.d.ts +27 -0
- package/lib/typescript/shared/types/bounds.types.d.ts.map +1 -1
- package/lib/typescript/shared/utils/bounds/navigation/zoom/build.d.ts.map +1 -1
- package/lib/typescript/shared/utils/bounds/navigation/zoom/config.d.ts +1 -1
- package/package.json +1 -1
- package/src/shared/providers/screen/styles/helpers/resolve-slot-styles/index.ts +140 -19
- package/src/shared/providers/screen/styles/helpers/resolve-slot-styles/materialize-slot.ts +5 -1
- package/src/shared/providers/screen/styles/helpers/resolve-slot-styles/types.ts +4 -0
- package/src/shared/providers/screen/styles/hooks/use-interpolated-style-maps.tsx +132 -44
- package/src/shared/providers/screen/styles/hooks/use-resolved-slot-style-map.tsx +7 -5
- package/src/shared/providers/screen/styles/styles.provider.tsx +2 -2
- package/src/shared/stores/bounds/internals/links.ts +27 -3
- package/src/shared/types/bounds.types.ts +27 -0
- package/src/shared/utils/bounds/navigation/zoom/build.ts +68 -73
- package/src/shared/utils/bounds/navigation/zoom/config.ts +1 -1
|
@@ -3,11 +3,13 @@ import { NO_STYLES } from "../../../../constants";
|
|
|
3
3
|
import { SystemStore } from "../../../../stores/system.store";
|
|
4
4
|
import type {
|
|
5
5
|
NormalizedTransitionInterpolatedStyle,
|
|
6
|
+
ScreenStyleInterpolator,
|
|
6
7
|
TransitionInterpolatedStyle,
|
|
7
8
|
} from "../../../../types/animation.types";
|
|
8
9
|
import { logger } from "../../../../utils/logger";
|
|
9
10
|
import { useScreenAnimationContext } from "../../animation";
|
|
10
11
|
import { useBuildBoundsAccessor } from "../../animation/helpers/accessors/use-build-bounds-accessor";
|
|
12
|
+
import type { ScreenInterpolatorFrame } from "../../animation/helpers/pipeline";
|
|
11
13
|
import { syncSelectedInterpolatorOptions } from "../../animation/helpers/selected-interpolator-options";
|
|
12
14
|
import { useDescriptorDerivations } from "../../descriptors";
|
|
13
15
|
import {
|
|
@@ -16,10 +18,93 @@ import {
|
|
|
16
18
|
} from "../../options";
|
|
17
19
|
import { normalizeSlots } from "../helpers/normalize-slots";
|
|
18
20
|
import { preserveAnimatedPropsOnly } from "../helpers/preserve-animated-props-only";
|
|
21
|
+
import type { LocalStyleLayers } from "../helpers/resolve-slot-styles";
|
|
19
22
|
import { stripInterpolatorOptions } from "../helpers/strip-interpolator-options";
|
|
20
23
|
|
|
24
|
+
const NO_STYLE_LAYERS: LocalStyleLayers = [];
|
|
25
|
+
|
|
26
|
+
type InterpolatorResult = {
|
|
27
|
+
stylesMap: NormalizedTransitionInterpolatedStyle;
|
|
28
|
+
rawStyleMap: TransitionInterpolatedStyle | undefined;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
type RunInterpolatorParams = {
|
|
32
|
+
interpolator: ScreenStyleInterpolator | undefined;
|
|
33
|
+
props: ScreenInterpolatorFrame;
|
|
34
|
+
progress: ScreenInterpolatorFrame["progress"];
|
|
35
|
+
next: ScreenInterpolatorFrame["next"];
|
|
36
|
+
bounds: Parameters<ScreenStyleInterpolator>[0]["bounds"];
|
|
37
|
+
shouldDeferStyleBuckets: boolean;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const normalizeRawStyleMap = (
|
|
41
|
+
rawStyleMap: TransitionInterpolatedStyle | undefined,
|
|
42
|
+
shouldDeferStyleBuckets: boolean,
|
|
43
|
+
) => {
|
|
44
|
+
"worklet";
|
|
45
|
+
|
|
46
|
+
if (!rawStyleMap) {
|
|
47
|
+
return NO_STYLES;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const stylesMap = normalizeSlots(stripInterpolatorOptions(rawStyleMap));
|
|
51
|
+
|
|
52
|
+
return shouldDeferStyleBuckets
|
|
53
|
+
? preserveAnimatedPropsOnly(stylesMap)
|
|
54
|
+
: stylesMap;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const runInterpolator = ({
|
|
58
|
+
interpolator,
|
|
59
|
+
props,
|
|
60
|
+
progress,
|
|
61
|
+
next,
|
|
62
|
+
bounds,
|
|
63
|
+
shouldDeferStyleBuckets,
|
|
64
|
+
}: RunInterpolatorParams): InterpolatorResult | undefined => {
|
|
65
|
+
"worklet";
|
|
66
|
+
|
|
67
|
+
if (!interpolator) {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const raw = interpolator({
|
|
73
|
+
...props,
|
|
74
|
+
progress,
|
|
75
|
+
next,
|
|
76
|
+
bounds,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const rawStyleMap: TransitionInterpolatedStyle | undefined =
|
|
80
|
+
typeof raw === "object" && raw != null ? raw : undefined;
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
rawStyleMap,
|
|
84
|
+
stylesMap: normalizeRawStyleMap(rawStyleMap, shouldDeferStyleBuckets),
|
|
85
|
+
};
|
|
86
|
+
} catch (_) {
|
|
87
|
+
if (__DEV__) {
|
|
88
|
+
logger.warn("screenStyleInterpolator must be a worklet");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const appendLayer = (
|
|
96
|
+
layers: LocalStyleLayers,
|
|
97
|
+
result: InterpolatorResult | undefined,
|
|
98
|
+
) => {
|
|
99
|
+
"worklet";
|
|
100
|
+
|
|
101
|
+
if (result) {
|
|
102
|
+
layers.push(result.stylesMap);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
21
106
|
/**
|
|
22
|
-
* Builds the raw interpolated
|
|
107
|
+
* Builds the raw interpolated style layers for the current screen pass.
|
|
23
108
|
*
|
|
24
109
|
* This hook exists to stabilize style ownership during rapid navigation,
|
|
25
110
|
* especially when an interactive close gesture overlaps with a new navigation
|
|
@@ -39,9 +124,9 @@ import { stripInterpolatorOptions } from "../helpers/strip-interpolator-options"
|
|
|
39
124
|
* hidden window so animated props and runtime options stay warm, but defer style
|
|
40
125
|
* buckets so measurement sees the final untransformed layout.
|
|
41
126
|
*
|
|
42
|
-
* The result
|
|
43
|
-
* slot ids determine whether
|
|
44
|
-
*
|
|
127
|
+
* The result is ordered from lowest to highest priority. Resolution happens
|
|
128
|
+
* downstream, where slot ids determine whether slots inherit from ancestors and
|
|
129
|
+
* where higher owner layers override lower owner layers per key.
|
|
45
130
|
*/
|
|
46
131
|
export const useInterpolatedStylesMap = () => {
|
|
47
132
|
const { currentScreenKey } = useDescriptorDerivations();
|
|
@@ -61,7 +146,7 @@ export const useInterpolatedStylesMap = () => {
|
|
|
61
146
|
|
|
62
147
|
const isGesturingDuringCloseAnimation = useSharedValue(false);
|
|
63
148
|
|
|
64
|
-
return useDerivedValue<
|
|
149
|
+
return useDerivedValue<LocalStyleLayers>(() => {
|
|
65
150
|
"worklet";
|
|
66
151
|
screenInterpolatorPropsRevision.get();
|
|
67
152
|
const props = screenInterpolatorProps.get();
|
|
@@ -99,16 +184,6 @@ export const useInterpolatedStylesMap = () => {
|
|
|
99
184
|
|
|
100
185
|
const interpolatorOptionsOwner =
|
|
101
186
|
isInGestureMode || !nextInterpolator ? "current" : "next";
|
|
102
|
-
const interpolator =
|
|
103
|
-
interpolatorOptionsOwner === "current"
|
|
104
|
-
? currentInterpolator
|
|
105
|
-
: nextInterpolator;
|
|
106
|
-
|
|
107
|
-
if (!interpolator) {
|
|
108
|
-
syncSelectedInterpolatorOptions(selectedInterpolatorOptions, "current");
|
|
109
|
-
syncScreenOptionsOverrides(undefined, screenOptions);
|
|
110
|
-
return NO_STYLES;
|
|
111
|
-
}
|
|
112
187
|
|
|
113
188
|
let effectiveProgress = progress;
|
|
114
189
|
let effectiveNext = next;
|
|
@@ -118,42 +193,55 @@ export const useInterpolatedStylesMap = () => {
|
|
|
118
193
|
effectiveNext = undefined;
|
|
119
194
|
}
|
|
120
195
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const rawStyleMap: TransitionInterpolatedStyle | undefined =
|
|
130
|
-
typeof raw === "object" && raw != null ? raw : undefined;
|
|
196
|
+
const currentResult = runInterpolator({
|
|
197
|
+
interpolator: currentInterpolator,
|
|
198
|
+
props,
|
|
199
|
+
progress: effectiveProgress,
|
|
200
|
+
next: effectiveNext,
|
|
201
|
+
bounds: boundsAccessor,
|
|
202
|
+
shouldDeferStyleBuckets,
|
|
203
|
+
});
|
|
131
204
|
|
|
205
|
+
if (interpolatorOptionsOwner === "current") {
|
|
132
206
|
syncSelectedInterpolatorOptions(
|
|
133
207
|
selectedInterpolatorOptions,
|
|
134
|
-
|
|
135
|
-
rawStyleMap?.options,
|
|
136
|
-
);
|
|
137
|
-
syncScreenOptionsOverrides(
|
|
138
|
-
interpolatorOptionsOwner === "current" ? rawStyleMap : undefined,
|
|
139
|
-
screenOptions,
|
|
208
|
+
"current",
|
|
209
|
+
currentResult?.rawStyleMap?.options,
|
|
140
210
|
);
|
|
211
|
+
syncScreenOptionsOverrides(currentResult?.rawStyleMap, screenOptions);
|
|
141
212
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
: normalizeSlots(stripInterpolatorOptions(rawStyleMap));
|
|
145
|
-
|
|
146
|
-
return shouldDeferStyleBuckets
|
|
147
|
-
? preserveAnimatedPropsOnly(stylesMap)
|
|
148
|
-
: stylesMap;
|
|
149
|
-
} catch (_) {
|
|
150
|
-
if (__DEV__) {
|
|
151
|
-
logger.warn("screenStyleInterpolator must be a worklet");
|
|
213
|
+
if (!currentResult) {
|
|
214
|
+
return NO_STYLE_LAYERS;
|
|
152
215
|
}
|
|
153
216
|
|
|
154
|
-
|
|
155
|
-
syncScreenOptionsOverrides(undefined, screenOptions);
|
|
156
|
-
return NO_STYLES;
|
|
217
|
+
return [currentResult.stylesMap];
|
|
157
218
|
}
|
|
219
|
+
|
|
220
|
+
const nextResult = runInterpolator({
|
|
221
|
+
interpolator: nextInterpolator,
|
|
222
|
+
props,
|
|
223
|
+
progress: effectiveProgress,
|
|
224
|
+
next: effectiveNext,
|
|
225
|
+
bounds: boundsAccessor,
|
|
226
|
+
shouldDeferStyleBuckets,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
syncSelectedInterpolatorOptions(
|
|
230
|
+
selectedInterpolatorOptions,
|
|
231
|
+
"next",
|
|
232
|
+
nextResult?.rawStyleMap?.options,
|
|
233
|
+
);
|
|
234
|
+
syncScreenOptionsOverrides(undefined, screenOptions);
|
|
235
|
+
|
|
236
|
+
const layers: LocalStyleLayers = [];
|
|
237
|
+
|
|
238
|
+
appendLayer(layers, currentResult);
|
|
239
|
+
appendLayer(layers, nextResult);
|
|
240
|
+
|
|
241
|
+
if (layers.length === 0) {
|
|
242
|
+
return NO_STYLE_LAYERS;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return layers;
|
|
158
246
|
});
|
|
159
247
|
};
|
|
@@ -7,18 +7,19 @@ import { NO_STYLES } from "../../../../constants";
|
|
|
7
7
|
import type { NormalizedTransitionInterpolatedStyle } from "../../../../types/animation.types";
|
|
8
8
|
import { useScreenAnimationContext } from "../../animation";
|
|
9
9
|
import {
|
|
10
|
+
type LocalStyleLayers,
|
|
10
11
|
type ResettableStyleStatesBySlot,
|
|
11
12
|
resolveSlotStyles,
|
|
12
13
|
reuseEqualResolvedSlots,
|
|
13
14
|
} from "../helpers/resolve-slot-styles";
|
|
14
15
|
|
|
15
16
|
interface UseResolvedStylesMapParams {
|
|
16
|
-
|
|
17
|
+
localStylesMaps: SharedValue<LocalStyleLayers>;
|
|
17
18
|
ancestorStylesMap?: SharedValue<NormalizedTransitionInterpolatedStyle>;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
export const useResolvedStylesMap = ({
|
|
21
|
-
|
|
22
|
+
localStylesMaps,
|
|
22
23
|
ancestorStylesMap,
|
|
23
24
|
}: UseResolvedStylesMapParams) => {
|
|
24
25
|
const { screenInterpolatorProps, screenInterpolatorPropsRevision } =
|
|
@@ -34,13 +35,14 @@ export const useResolvedStylesMap = ({
|
|
|
34
35
|
screenInterpolatorPropsRevision.get();
|
|
35
36
|
|
|
36
37
|
const props = screenInterpolatorProps.get();
|
|
37
|
-
// Keep missing local slots alive
|
|
38
|
-
//
|
|
38
|
+
// Keep missing local slots alive only when another route drives this screen
|
|
39
|
+
// and no active local style layer is available. Once a current/next layer
|
|
40
|
+
// runs, omitted local slots and dropped keys are intentional reset signals.
|
|
39
41
|
const deferLocalSlotResets = !props.focused && !props.current.closing;
|
|
40
42
|
|
|
41
43
|
const { resolvedStylesMap, nextPreviousStyleStatesBySlot } =
|
|
42
44
|
resolveSlotStyles({
|
|
43
|
-
|
|
45
|
+
localStylesMaps: localStylesMaps.get(),
|
|
44
46
|
ancestorStylesMap: ancestorStylesMap?.get() ?? NO_STYLES,
|
|
45
47
|
previousStyleStatesBySlot: previousStyleStatesBySlot.get(),
|
|
46
48
|
deferLocalSlotResets,
|
|
@@ -26,10 +26,10 @@ export const {
|
|
|
26
26
|
})<Props, ScreenStylesContextValue>(({ children, isFloatingOverlay }) => {
|
|
27
27
|
const parentContext = useContext(ScreenStylesContext);
|
|
28
28
|
|
|
29
|
-
const
|
|
29
|
+
const rawStylesMaps = useInterpolatedStylesMap();
|
|
30
30
|
|
|
31
31
|
const stylesMap = useResolvedStylesMap({
|
|
32
|
-
|
|
32
|
+
localStylesMaps: rawStylesMaps,
|
|
33
33
|
ancestorStylesMap: parentContext?.stylesMap,
|
|
34
34
|
});
|
|
35
35
|
|
|
@@ -216,6 +216,20 @@ const isCompletedLink = (link: TagLink | null): link is TagLink => {
|
|
|
216
216
|
return !!link?.destination;
|
|
217
217
|
};
|
|
218
218
|
|
|
219
|
+
const getPendingSourceLink = (
|
|
220
|
+
state: LinkPairsState,
|
|
221
|
+
pairKey: ScreenPairKey,
|
|
222
|
+
linkKey: LinkKey,
|
|
223
|
+
): TagLink | null => {
|
|
224
|
+
"worklet";
|
|
225
|
+
const sourceScreenKey = getSourceScreenKeyFromPairKey(pairKey);
|
|
226
|
+
const pendingPairKey = createPendingPairKey(sourceScreenKey);
|
|
227
|
+
|
|
228
|
+
if (pendingPairKey === pairKey) return null;
|
|
229
|
+
|
|
230
|
+
return getPairLink(state, pendingPairKey, linkKey);
|
|
231
|
+
};
|
|
232
|
+
|
|
219
233
|
function getResolvedLink(
|
|
220
234
|
pairKey: ScreenPairKey,
|
|
221
235
|
tag: TagID,
|
|
@@ -226,12 +240,22 @@ function getResolvedLink(
|
|
|
226
240
|
const group = getGroupKeyFromTag(tag);
|
|
227
241
|
const requestedLink = getPairLink(state, pairKey, linkKey);
|
|
228
242
|
|
|
243
|
+
// Press-triggered zoom captures the source before navigation under a pending
|
|
244
|
+
// source<> pair. If the destination screen has no Boundary.View, nothing
|
|
245
|
+
// promotes that source into source<>destination, but zoom can still animate to
|
|
246
|
+
// its default top target from the pending source bounds.
|
|
247
|
+
const fallbackPendingLink = requestedLink
|
|
248
|
+
? null
|
|
249
|
+
: getPendingSourceLink(state, pairKey, linkKey);
|
|
250
|
+
|
|
251
|
+
const link = requestedLink ?? fallbackPendingLink;
|
|
252
|
+
|
|
229
253
|
// Group active ids can update before the new member has a full source/destination
|
|
230
254
|
// link, so unresolved grouped links fall back to the initial id's measurements.
|
|
231
|
-
if (!group || isCompletedLink(
|
|
255
|
+
if (!group || isCompletedLink(link)) {
|
|
232
256
|
return {
|
|
233
257
|
tag,
|
|
234
|
-
link
|
|
258
|
+
link,
|
|
235
259
|
};
|
|
236
260
|
}
|
|
237
261
|
|
|
@@ -248,7 +272,7 @@ function getResolvedLink(
|
|
|
248
272
|
|
|
249
273
|
return {
|
|
250
274
|
tag,
|
|
251
|
-
link
|
|
275
|
+
link,
|
|
252
276
|
};
|
|
253
277
|
}
|
|
254
278
|
|
|
@@ -78,6 +78,33 @@ export type BoundsNavigationZoomOptions = {
|
|
|
78
78
|
* animates above it.
|
|
79
79
|
*/
|
|
80
80
|
backgroundScale?: number;
|
|
81
|
+
/**
|
|
82
|
+
* Maximum dynamic gesture sensitivity applied by zoom.
|
|
83
|
+
*
|
|
84
|
+
* Zoom lowers gesture sensitivity as the drag gets deeper so the content
|
|
85
|
+
* handoff stays stable. This value controls the starting/highest sensitivity
|
|
86
|
+
* in that curve.
|
|
87
|
+
*
|
|
88
|
+
* @default 0.8
|
|
89
|
+
*/
|
|
90
|
+
maxSensitivity?: number;
|
|
91
|
+
/**
|
|
92
|
+
* Velocity-driven depth applied to the dismiss scale handoff.
|
|
93
|
+
*
|
|
94
|
+
* Higher values make fast releases orbit farther around the final scale. Set
|
|
95
|
+
* to `0` to remove the velocity depth effect.
|
|
96
|
+
*
|
|
97
|
+
* @default 0.5
|
|
98
|
+
*/
|
|
99
|
+
velocityDepth?: number;
|
|
100
|
+
/**
|
|
101
|
+
* Whether gesture displacement should drive transition progress or remain as
|
|
102
|
+
* freeform gesture values. Zoom defaults to `"freeform"` so drag can move the
|
|
103
|
+
* content without owning the whole screen progress.
|
|
104
|
+
*
|
|
105
|
+
* @default "freeform"
|
|
106
|
+
*/
|
|
107
|
+
gestureProgressMode?: GestureProgressMode;
|
|
81
108
|
/**
|
|
82
109
|
* Horizontal gesture drag scaling curve, applied when the active dismiss
|
|
83
110
|
* direction is horizontal.
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { interpolate } from "react-native-reanimated";
|
|
2
2
|
import {
|
|
3
3
|
EPSILON,
|
|
4
|
-
NAVIGATION_MASK_CONTAINER_STYLE_ID,
|
|
5
4
|
NAVIGATION_MASK_ELEMENT_STYLE_ID,
|
|
6
5
|
VISIBLE_STYLE,
|
|
7
6
|
} from "../../../../constants";
|
|
@@ -18,6 +17,10 @@ import {
|
|
|
18
17
|
resolveDirectionalDragScale,
|
|
19
18
|
resolveOpacityRangeTuple,
|
|
20
19
|
} from "../math";
|
|
20
|
+
import {
|
|
21
|
+
resolveDismissScaleHandoff,
|
|
22
|
+
resolveRevealGestureHandoff,
|
|
23
|
+
} from "../reveal/math";
|
|
21
24
|
import {
|
|
22
25
|
ZOOM_DISMISS_SCALE_ORBIT_DEPTH,
|
|
23
26
|
ZOOM_FOCUSED_ELEMENT_CLOSE_OPACITY_RANGE,
|
|
@@ -39,71 +42,30 @@ import type { BuildZoomStylesParams, ZoomInterpolatedStyle } from "./types";
|
|
|
39
42
|
|
|
40
43
|
const IDENTITY_DRAG_SCALE_OUTPUT = [1, 1] as const;
|
|
41
44
|
|
|
42
|
-
function resolveZoomGestureHandoff(rawDrag: number) {
|
|
43
|
-
"worklet";
|
|
44
|
-
|
|
45
|
-
const gestureSensitivity = interpolate(rawDrag, [0, 1], [0.7, 0.1], "clamp");
|
|
46
|
-
const releaseBoost = interpolate(rawDrag, [0, 1], [1, 1.4], "clamp");
|
|
47
|
-
const releaseSensitivity = interpolate(
|
|
48
|
-
gestureSensitivity,
|
|
49
|
-
[0.28, 0.9],
|
|
50
|
-
[0.7, 1],
|
|
51
|
-
"clamp",
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
return {
|
|
55
|
-
gestureSensitivity,
|
|
56
|
-
gestureReleaseVelocityScale: releaseBoost * releaseSensitivity,
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
|
|
60
45
|
function resolveZoomGestureOptions({
|
|
61
46
|
rawDrag,
|
|
62
|
-
|
|
47
|
+
maxSensitivity,
|
|
48
|
+
gestureProgressMode,
|
|
63
49
|
}: {
|
|
64
50
|
rawDrag: number;
|
|
65
|
-
|
|
51
|
+
maxSensitivity: number;
|
|
52
|
+
gestureProgressMode: ScreenTransitionOptions["gestureProgressMode"];
|
|
66
53
|
}) {
|
|
67
54
|
"worklet";
|
|
68
55
|
|
|
69
56
|
const { gestureSensitivity, gestureReleaseVelocityScale } =
|
|
70
|
-
|
|
57
|
+
resolveRevealGestureHandoff({
|
|
58
|
+
rawDrag,
|
|
59
|
+
maxSensitivity,
|
|
60
|
+
});
|
|
71
61
|
|
|
72
62
|
return {
|
|
73
|
-
gestureProgressMode
|
|
74
|
-
gestureSensitivity
|
|
75
|
-
gestureReleaseVelocityScale
|
|
76
|
-
activeOptions.gestureReleaseVelocityScale ?? gestureReleaseVelocityScale,
|
|
63
|
+
gestureProgressMode,
|
|
64
|
+
gestureSensitivity,
|
|
65
|
+
gestureReleaseVelocityScale,
|
|
77
66
|
};
|
|
78
67
|
}
|
|
79
68
|
|
|
80
|
-
function resolveZoomDismissScaleHandoff({
|
|
81
|
-
progress,
|
|
82
|
-
releaseScale,
|
|
83
|
-
targetScale,
|
|
84
|
-
rawDrag,
|
|
85
|
-
}: {
|
|
86
|
-
progress: number;
|
|
87
|
-
releaseScale: number;
|
|
88
|
-
targetScale: number;
|
|
89
|
-
rawDrag: number;
|
|
90
|
-
}) {
|
|
91
|
-
"worklet";
|
|
92
|
-
|
|
93
|
-
const closeProgress = 1 - progress;
|
|
94
|
-
const scaleProgress = Math.sin((Math.PI / 2) * closeProgress);
|
|
95
|
-
const baseScale = releaseScale + (targetScale - releaseScale) * scaleProgress;
|
|
96
|
-
const orbitDepth = interpolate(
|
|
97
|
-
rawDrag,
|
|
98
|
-
[0, 1],
|
|
99
|
-
[0, ZOOM_DISMISS_SCALE_ORBIT_DEPTH],
|
|
100
|
-
"clamp",
|
|
101
|
-
);
|
|
102
|
-
const orbitScale = 1 - orbitDepth * Math.sin(Math.PI * closeProgress);
|
|
103
|
-
|
|
104
|
-
return baseScale * orbitScale;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
69
|
/* -------------------------------------------------------------------------- */
|
|
108
70
|
/* BUILD ZOOM STYLES */
|
|
109
71
|
/* -------------------------------------------------------------------------- */
|
|
@@ -168,9 +130,11 @@ export function buildZoomStyles({
|
|
|
168
130
|
style: VISIBLE_STYLE,
|
|
169
131
|
},
|
|
170
132
|
} satisfies TransitionInterpolatedStyle;
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
133
|
+
const navigationMaskEnabled = props.current.options.navigationMaskEnabled;
|
|
134
|
+
const maxSensitivity = zoomOptions?.maxSensitivity ?? 0.8;
|
|
135
|
+
const velocityDepth =
|
|
136
|
+
zoomOptions?.velocityDepth ?? ZOOM_DISMISS_SCALE_ORBIT_DEPTH;
|
|
137
|
+
const gestureProgressMode = zoomOptions?.gestureProgressMode ?? "freeform";
|
|
174
138
|
|
|
175
139
|
/* --------------------------- Gesture / Drag Values ------------------------- */
|
|
176
140
|
|
|
@@ -238,16 +202,18 @@ export function buildZoomStyles({
|
|
|
238
202
|
: IDENTITY_DRAG_SCALE_OUTPUT[1];
|
|
239
203
|
const dragScale = combineScales(dragXScale, dragYScale);
|
|
240
204
|
const handoffDragScale = props.active.gesture.dismissing
|
|
241
|
-
?
|
|
205
|
+
? resolveDismissScaleHandoff({
|
|
242
206
|
progress: props.active.progress,
|
|
243
207
|
releaseScale: dragScale,
|
|
244
208
|
targetScale: 1,
|
|
245
|
-
|
|
209
|
+
velocity: props.active.gesture.velocity,
|
|
210
|
+
velocityDepth,
|
|
246
211
|
})
|
|
247
212
|
: dragScale;
|
|
248
213
|
const zoomGestureOptions = resolveZoomGestureOptions({
|
|
249
214
|
rawDrag,
|
|
250
|
-
|
|
215
|
+
maxSensitivity,
|
|
216
|
+
gestureProgressMode,
|
|
251
217
|
});
|
|
252
218
|
|
|
253
219
|
/* ----------------------------- Focused Screen ----------------------------- */
|
|
@@ -301,11 +267,32 @@ export function buildZoomStyles({
|
|
|
301
267
|
const maskWidth = Math.max(1, toNumber(maskRaw.width) + left + right);
|
|
302
268
|
const maskHeight = Math.max(1, toNumber(maskRaw.height) + top + bottom);
|
|
303
269
|
|
|
304
|
-
const
|
|
305
|
-
const
|
|
306
|
-
const
|
|
307
|
-
const
|
|
308
|
-
|
|
270
|
+
const contentBaseTranslateX = toNumber(contentRaw.translateX);
|
|
271
|
+
const contentBaseTranslateY = toNumber(contentRaw.translateY);
|
|
272
|
+
const contentBaseScale = toNumber(contentRaw.scale, 1);
|
|
273
|
+
const safeContentBaseScale =
|
|
274
|
+
Math.abs(contentBaseScale) > EPSILON ? contentBaseScale : 1;
|
|
275
|
+
const contentTranslateX = contentBaseTranslateX + dragX;
|
|
276
|
+
const contentTranslateY = contentBaseTranslateY + dragY;
|
|
277
|
+
const contentScale = contentBaseScale * handoffDragScale;
|
|
278
|
+
|
|
279
|
+
const maskBaseTranslateX = toNumber(maskRaw.translateX) - left;
|
|
280
|
+
const maskBaseTranslateY = toNumber(maskRaw.translateY) - top;
|
|
281
|
+
const maskCenterX = maskWidth / 2;
|
|
282
|
+
const maskCenterY = maskHeight / 2;
|
|
283
|
+
const contentCenterX = screenLayout.width / 2;
|
|
284
|
+
const contentCenterY = screenLayout.height / 2;
|
|
285
|
+
const compensatedMaskTranslateX =
|
|
286
|
+
(maskBaseTranslateX -
|
|
287
|
+
contentBaseTranslateX +
|
|
288
|
+
(1 - contentBaseScale) * (maskCenterX - contentCenterX)) /
|
|
289
|
+
safeContentBaseScale;
|
|
290
|
+
const compensatedMaskTranslateY =
|
|
291
|
+
(maskBaseTranslateY -
|
|
292
|
+
contentBaseTranslateY +
|
|
293
|
+
(1 - contentBaseScale) * (maskCenterY - contentCenterY)) /
|
|
294
|
+
safeContentBaseScale;
|
|
295
|
+
const compensatedMaskScale = 1 / safeContentBaseScale;
|
|
309
296
|
|
|
310
297
|
const focusedContentStyle = {
|
|
311
298
|
opacity: zoomOptions?.debug ? 0.5 : focusedFade,
|
|
@@ -314,27 +301,31 @@ export function buildZoomStyles({
|
|
|
314
301
|
{ translateY: contentTranslateY },
|
|
315
302
|
{ scale: contentScale },
|
|
316
303
|
],
|
|
317
|
-
|
|
318
|
-
|
|
304
|
+
...(navigationMaskEnabled
|
|
305
|
+
? {}
|
|
306
|
+
: {
|
|
307
|
+
borderRadius: focusedMaskBorderRadius,
|
|
308
|
+
overflow: "hidden" as const,
|
|
309
|
+
}),
|
|
319
310
|
};
|
|
320
311
|
|
|
321
312
|
const focusedStyles: ZoomInterpolatedStyle = {
|
|
322
|
-
|
|
313
|
+
content: {
|
|
323
314
|
style: focusedContentStyle,
|
|
324
315
|
},
|
|
325
316
|
...sourceVisibilityStyle,
|
|
326
317
|
};
|
|
327
318
|
|
|
328
|
-
if (
|
|
319
|
+
if (navigationMaskEnabled) {
|
|
329
320
|
focusedStyles[NAVIGATION_MASK_ELEMENT_STYLE_ID] = {
|
|
330
321
|
style: {
|
|
331
322
|
width: maskWidth,
|
|
332
323
|
height: maskHeight,
|
|
333
324
|
borderRadius: focusedMaskBorderRadius,
|
|
334
325
|
transform: [
|
|
335
|
-
{ translateX:
|
|
336
|
-
{ translateY:
|
|
337
|
-
{ scale:
|
|
326
|
+
{ translateX: compensatedMaskTranslateX },
|
|
327
|
+
{ translateY: compensatedMaskTranslateY },
|
|
328
|
+
{ scale: compensatedMaskScale },
|
|
338
329
|
],
|
|
339
330
|
},
|
|
340
331
|
};
|
|
@@ -416,6 +407,10 @@ export function buildZoomStyles({
|
|
|
416
407
|
Math.abs(unfocusedContentScale),
|
|
417
408
|
EPSILON,
|
|
418
409
|
);
|
|
410
|
+
// A naturally settled close can leave this as the last emitted style frame,
|
|
411
|
+
// so drop temporary stacking here instead of waiting for a later reset pass.
|
|
412
|
+
const shouldElevateUnfocusedElement =
|
|
413
|
+
!props.active.closing || !props.active.logicallySettled;
|
|
419
414
|
|
|
420
415
|
const scaleShiftX = computeCenterScaleShift({
|
|
421
416
|
center: elementCenterX,
|
|
@@ -480,8 +475,8 @@ export function buildZoomStyles({
|
|
|
480
475
|
},
|
|
481
476
|
],
|
|
482
477
|
opacity: zoomOptions?.debug ? 1 : unfocusedFade,
|
|
483
|
-
zIndex: 9999,
|
|
484
|
-
elevation: 9999,
|
|
478
|
+
zIndex: shouldElevateUnfocusedElement ? 9999 : 0,
|
|
479
|
+
elevation: shouldElevateUnfocusedElement ? 9999 : 0,
|
|
485
480
|
};
|
|
486
481
|
|
|
487
482
|
return {
|
|
@@ -9,7 +9,7 @@ export const ZOOM_DRAG_DIRECTIONAL_SCALE_EXPONENT = 2;
|
|
|
9
9
|
export const ZOOM_DRAG_TRANSLATION_NEGATIVE_MAX = 1;
|
|
10
10
|
export const ZOOM_DRAG_TRANSLATION_POSITIVE_MAX = 1;
|
|
11
11
|
export const ZOOM_DRAG_TRANSLATION_EXPONENT = 1;
|
|
12
|
-
export const ZOOM_DISMISS_SCALE_ORBIT_DEPTH = 0.
|
|
12
|
+
export const ZOOM_DISMISS_SCALE_ORBIT_DEPTH = 0.5;
|
|
13
13
|
export const ZOOM_BACKGROUND_SCALE = 0.9375;
|
|
14
14
|
export const ZOOM_FOCUSED_ELEMENT_OPEN_OPACITY_RANGE = [0, 0.5, 0, 1] as const;
|
|
15
15
|
export const ZOOM_FOCUSED_ELEMENT_CLOSE_OPACITY_RANGE = [0.6, 1, 0, 1] as const;
|