react-native-screen-transitions 2.0.2 → 2.0.4
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/utils/bounds/constants.js +3 -3
- package/lib/commonjs/utils/bounds/constants.js.map +1 -1
- package/lib/commonjs/utils/bounds/get-bounds.js +3 -3
- package/lib/commonjs/utils/bounds/get-bounds.js.map +1 -1
- package/lib/module/utils/bounds/constants.js +3 -3
- package/lib/module/utils/bounds/constants.js.map +1 -1
- package/lib/module/utils/bounds/get-bounds.js +3 -3
- package/lib/module/utils/bounds/get-bounds.js.map +1 -1
- package/lib/typescript/utils/bounds/_types/get-bounds.d.ts +2 -2
- package/lib/typescript/utils/bounds/_types/get-bounds.d.ts.map +1 -1
- package/lib/typescript/utils/bounds/constants.d.ts +4 -4
- package/lib/typescript/utils/bounds/constants.d.ts.map +1 -1
- package/lib/typescript/utils/bounds/get-bounds.d.ts +2 -2
- package/lib/typescript/utils/bounds/get-bounds.d.ts.map +1 -1
- package/lib/typescript/utils/bounds/index.d.ts +1 -1
- package/lib/typescript/utils/bounds/index.d.ts.map +1 -1
- package/package.json +4 -2
- package/src/__tests__/geometry.test.ts +127 -0
- package/src/components/bounds-activator.tsx +29 -0
- package/src/components/controllers/screen-lifecycle.tsx +72 -0
- package/src/components/create-transition-aware-component.tsx +99 -0
- package/src/components/root-transition-aware.tsx +56 -0
- package/src/configs/index.ts +2 -0
- package/src/configs/presets.ts +227 -0
- package/src/configs/specs.ts +9 -0
- package/src/hooks/animation/use-associated-style.tsx +28 -0
- package/src/hooks/animation/use-screen-animation.tsx +142 -0
- package/src/hooks/bounds/use-bound-measurer.tsx +71 -0
- package/src/hooks/gestures/use-build-gestures.tsx +369 -0
- package/src/hooks/gestures/use-scroll-progress.tsx +60 -0
- package/src/hooks/use-stable-callback.tsx +15 -0
- package/src/index.ts +32 -0
- package/src/integrations/native-stack/navigators/createNativeStackNavigator.tsx +112 -0
- package/src/integrations/native-stack/utils/debounce.tsx +14 -0
- package/src/integrations/native-stack/utils/getModalRoutesKeys.ts +21 -0
- package/src/integrations/native-stack/utils/useAnimatedHeaderHeight.tsx +18 -0
- package/src/integrations/native-stack/utils/useDismissedRouteError.tsx +30 -0
- package/src/integrations/native-stack/utils/useInvalidPreventRemoveError.tsx +31 -0
- package/src/integrations/native-stack/views/FontProcessor.native.tsx +12 -0
- package/src/integrations/native-stack/views/FontProcessor.tsx +5 -0
- package/src/integrations/native-stack/views/FooterComponent.tsx +10 -0
- package/src/integrations/native-stack/views/NativeStackView.native.tsx +657 -0
- package/src/integrations/native-stack/views/NativeStackView.tsx +214 -0
- package/src/integrations/native-stack/views/useHeaderConfigProps.tsx +295 -0
- package/src/providers/gestures.tsx +89 -0
- package/src/providers/keys.tsx +38 -0
- package/src/stores/animations.ts +45 -0
- package/src/stores/bounds.ts +71 -0
- package/src/stores/gestures.ts +55 -0
- package/src/stores/navigator-dismiss-state.ts +17 -0
- package/src/stores/utils/reset-stores-for-screen.ts +14 -0
- package/src/types/animation.ts +76 -0
- package/src/types/bounds.ts +82 -0
- package/src/types/core.ts +50 -0
- package/src/types/gesture.ts +33 -0
- package/src/types/navigator.ts +744 -0
- package/src/types/utils.ts +3 -0
- package/src/utils/animation/animate.ts +28 -0
- package/src/utils/animation/run-transition.ts +49 -0
- package/src/utils/bounds/_types/builder.ts +35 -0
- package/src/utils/bounds/_types/geometry.ts +17 -0
- package/src/utils/bounds/_types/get-bounds.ts +10 -0
- package/src/utils/bounds/build-bound-styles.ts +184 -0
- package/src/utils/bounds/constants.ts +25 -0
- package/src/utils/bounds/flatten-styles.ts +21 -0
- package/src/utils/bounds/geometry.ts +113 -0
- package/src/utils/bounds/get-bounds.ts +56 -0
- package/src/utils/bounds/index.ts +46 -0
- package/src/utils/bounds/style-composers.ts +172 -0
- package/src/utils/gesture/apply-gesture-activation-criteria.ts +109 -0
- package/src/utils/gesture/map-gesture-to-progress.ts +11 -0
- package/src/utils/gesture/normalize-gesture-translation.ts +20 -0
- package/src/utils/index.ts +1 -0
- package/lib/commonjs/__tests__ /geometry.test.js +0 -178
- package/lib/commonjs/__tests__ /geometry.test.js.map +0 -1
- package/lib/module/__tests__ /geometry.test.js +0 -178
- package/lib/module/__tests__ /geometry.test.js.map +0 -1
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Extrapolation,
|
|
3
|
+
interpolate,
|
|
4
|
+
interpolateColor,
|
|
5
|
+
} from "react-native-reanimated";
|
|
6
|
+
import type { ScreenTransitionConfig } from "../types/navigator";
|
|
7
|
+
import { DefaultSpec } from "./specs";
|
|
8
|
+
|
|
9
|
+
export const SlideFromTop = (
|
|
10
|
+
config: Partial<ScreenTransitionConfig> = {},
|
|
11
|
+
): ScreenTransitionConfig => {
|
|
12
|
+
return {
|
|
13
|
+
enableTransitions: true,
|
|
14
|
+
gestureEnabled: true,
|
|
15
|
+
gestureDirection: "vertical-inverted",
|
|
16
|
+
screenStyleInterpolator: ({
|
|
17
|
+
current,
|
|
18
|
+
next,
|
|
19
|
+
layouts: {
|
|
20
|
+
screen: { height },
|
|
21
|
+
},
|
|
22
|
+
}) => {
|
|
23
|
+
"worklet";
|
|
24
|
+
|
|
25
|
+
const progress = current.progress + (next?.progress ?? 0);
|
|
26
|
+
|
|
27
|
+
const y = interpolate(progress, [0, 1, 2], [-height, 0, height]);
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
contentStyle: {
|
|
31
|
+
transform: [{ translateY: y }],
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
transitionSpec: {
|
|
36
|
+
open: DefaultSpec,
|
|
37
|
+
close: DefaultSpec,
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
...config,
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const ZoomIn = (
|
|
45
|
+
config: Partial<ScreenTransitionConfig> = {},
|
|
46
|
+
): ScreenTransitionConfig => {
|
|
47
|
+
return {
|
|
48
|
+
enableTransitions: true,
|
|
49
|
+
gestureEnabled: false,
|
|
50
|
+
screenStyleInterpolator: ({ current, next }) => {
|
|
51
|
+
"worklet";
|
|
52
|
+
|
|
53
|
+
const progress = current.progress + (next?.progress ?? 0);
|
|
54
|
+
|
|
55
|
+
const scale = interpolate(
|
|
56
|
+
progress,
|
|
57
|
+
[0, 1, 2],
|
|
58
|
+
[0.5, 1, 0.5],
|
|
59
|
+
Extrapolation.CLAMP,
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const opacity = interpolate(
|
|
63
|
+
progress,
|
|
64
|
+
[0, 1, 2],
|
|
65
|
+
[0, 1, 0],
|
|
66
|
+
Extrapolation.CLAMP,
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
contentStyle: {
|
|
71
|
+
transform: [{ scale }],
|
|
72
|
+
opacity,
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
transitionSpec: {
|
|
77
|
+
open: DefaultSpec,
|
|
78
|
+
close: DefaultSpec,
|
|
79
|
+
},
|
|
80
|
+
...config,
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export const SlideFromBottom = (
|
|
85
|
+
config: Partial<ScreenTransitionConfig> = {},
|
|
86
|
+
): ScreenTransitionConfig => {
|
|
87
|
+
return {
|
|
88
|
+
enableTransitions: true,
|
|
89
|
+
gestureEnabled: true,
|
|
90
|
+
gestureDirection: "vertical",
|
|
91
|
+
screenStyleInterpolator: ({
|
|
92
|
+
current,
|
|
93
|
+
next,
|
|
94
|
+
layouts: {
|
|
95
|
+
screen: { height },
|
|
96
|
+
},
|
|
97
|
+
}) => {
|
|
98
|
+
"worklet";
|
|
99
|
+
|
|
100
|
+
const progress = current.progress + (next?.progress ?? 0);
|
|
101
|
+
|
|
102
|
+
const y = interpolate(progress, [0, 1, 2], [height, 0, -height]);
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
contentStyle: {
|
|
106
|
+
transform: [{ translateY: y }],
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
},
|
|
110
|
+
transitionSpec: {
|
|
111
|
+
open: DefaultSpec,
|
|
112
|
+
close: DefaultSpec,
|
|
113
|
+
},
|
|
114
|
+
...config,
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export const DraggableCard = (
|
|
119
|
+
config: Partial<ScreenTransitionConfig> = {},
|
|
120
|
+
): ScreenTransitionConfig => {
|
|
121
|
+
return {
|
|
122
|
+
enableTransitions: true,
|
|
123
|
+
gestureEnabled: true,
|
|
124
|
+
gestureDirection: ["horizontal", "vertical"],
|
|
125
|
+
screenStyleInterpolator: ({ current, progress, layouts: { screen } }) => {
|
|
126
|
+
"worklet";
|
|
127
|
+
|
|
128
|
+
/** Combined */
|
|
129
|
+
const scale = interpolate(progress, [0, 1, 2], [0, 1, 0.75]);
|
|
130
|
+
|
|
131
|
+
/** Vertical */
|
|
132
|
+
const translateY = interpolate(
|
|
133
|
+
current.gesture.normalizedY,
|
|
134
|
+
[-1, 1],
|
|
135
|
+
[-screen.height * 0.5, screen.height * 0.5],
|
|
136
|
+
"clamp",
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
/** Horizontal */
|
|
140
|
+
const translateX = interpolate(
|
|
141
|
+
current.gesture.normalizedX,
|
|
142
|
+
[-1, 1],
|
|
143
|
+
[-screen.width * 0.5, screen.width * 0.5],
|
|
144
|
+
"clamp",
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
contentStyle: {
|
|
149
|
+
transform: [{ scale }, { translateY: translateY }, { translateX }],
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
},
|
|
153
|
+
transitionSpec: {
|
|
154
|
+
open: DefaultSpec,
|
|
155
|
+
close: DefaultSpec,
|
|
156
|
+
},
|
|
157
|
+
...config,
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export const ElasticCard = (
|
|
162
|
+
config: Partial<ScreenTransitionConfig> & {
|
|
163
|
+
elasticFactor?: number;
|
|
164
|
+
} = { elasticFactor: 0.5 },
|
|
165
|
+
): ScreenTransitionConfig => {
|
|
166
|
+
return {
|
|
167
|
+
enableTransitions: true,
|
|
168
|
+
gestureEnabled: true,
|
|
169
|
+
gestureDirection: "bidirectional",
|
|
170
|
+
screenStyleInterpolator: ({
|
|
171
|
+
current,
|
|
172
|
+
next,
|
|
173
|
+
layouts: { screen },
|
|
174
|
+
progress,
|
|
175
|
+
}) => {
|
|
176
|
+
"worklet";
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Applies to both screens ( previous and incoming)
|
|
180
|
+
*/
|
|
181
|
+
|
|
182
|
+
const scale = interpolate(progress, [0, 1, 2], [0, 1, 0.8]);
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Applies to current screen
|
|
186
|
+
*/
|
|
187
|
+
const maxElasticityX = screen.width * (config.elasticFactor ?? 0.5);
|
|
188
|
+
const maxElasticityY = screen.height * (config.elasticFactor ?? 0.5);
|
|
189
|
+
const translateX = interpolate(
|
|
190
|
+
current.gesture.normalizedX,
|
|
191
|
+
[-1, 0, 1],
|
|
192
|
+
[-maxElasticityX, 0, maxElasticityX],
|
|
193
|
+
"clamp",
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
const translateY = interpolate(
|
|
197
|
+
current.gesture.normalizedY,
|
|
198
|
+
[-1, 0, 1],
|
|
199
|
+
[-maxElasticityY, 0, maxElasticityY],
|
|
200
|
+
"clamp",
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Applies to unfocused screen ( previous screen )
|
|
205
|
+
*/
|
|
206
|
+
const overlayColor = interpolateColor(
|
|
207
|
+
progress,
|
|
208
|
+
[0, 1],
|
|
209
|
+
["rgba(0,0,0,0)", "rgba(0,0,0,0.5)"],
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
contentStyle: {
|
|
214
|
+
transform: [{ scale }, { translateX }, { translateY }],
|
|
215
|
+
},
|
|
216
|
+
overlayStyle: {
|
|
217
|
+
backgroundColor: !next ? overlayColor : "rgba(0,0,0,0)",
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
},
|
|
221
|
+
transitionSpec: {
|
|
222
|
+
open: DefaultSpec,
|
|
223
|
+
close: DefaultSpec,
|
|
224
|
+
},
|
|
225
|
+
...config,
|
|
226
|
+
};
|
|
227
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { useAnimatedStyle } from "react-native-reanimated";
|
|
2
|
+
import { _useScreenAnimation } from "./use-screen-animation";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* This hook is used to get the associated styles for a given styleId.
|
|
6
|
+
* It is used to get the associated styles for a given styleId.
|
|
7
|
+
* It is used to get the associated styles for a given styleId.
|
|
8
|
+
*/
|
|
9
|
+
export const useAssociatedStyles = ({ id }: { id?: string } = {}) => {
|
|
10
|
+
const { screenStyleInterpolator, screenInterpolatorProps } =
|
|
11
|
+
_useScreenAnimation();
|
|
12
|
+
|
|
13
|
+
const associatedStyles = useAnimatedStyle(() => {
|
|
14
|
+
"worklet";
|
|
15
|
+
|
|
16
|
+
if (!id || !screenStyleInterpolator) {
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
screenStyleInterpolator(screenInterpolatorProps.value)[id] || {
|
|
22
|
+
opacity: 1, // <-- This fixes flickering?? We'll have to deep dive this?? wtf
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
return { associatedStyles };
|
|
28
|
+
};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import type { ParamListBase, RouteProp } from "@react-navigation/native";
|
|
2
|
+
import { useWindowDimensions } from "react-native";
|
|
3
|
+
import { type SharedValue, useDerivedValue } from "react-native-reanimated";
|
|
4
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
5
|
+
import { useKeys } from "../../providers/keys";
|
|
6
|
+
import { Animations } from "../../stores/animations";
|
|
7
|
+
import { Bounds } from "../../stores/bounds";
|
|
8
|
+
import { type GestureMap, Gestures } from "../../stores/gestures";
|
|
9
|
+
import type {
|
|
10
|
+
ScreenInterpolationProps,
|
|
11
|
+
ScreenTransitionState,
|
|
12
|
+
} from "../../types/animation";
|
|
13
|
+
import type { BoundEntry } from "../../types/bounds";
|
|
14
|
+
import type { NativeStackDescriptor } from "../../types/navigator";
|
|
15
|
+
import { buildBoundsAccessor } from "../../utils/bounds";
|
|
16
|
+
|
|
17
|
+
type BuiltState = {
|
|
18
|
+
progress: SharedValue<number>;
|
|
19
|
+
closing: SharedValue<number>;
|
|
20
|
+
animating: SharedValue<number>;
|
|
21
|
+
gesture: GestureMap;
|
|
22
|
+
route: RouteProp<ParamListBase>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const FALLBACK = Object.freeze({
|
|
26
|
+
progress: 0,
|
|
27
|
+
closing: 0,
|
|
28
|
+
animating: 0,
|
|
29
|
+
gesture: {
|
|
30
|
+
x: 0,
|
|
31
|
+
y: 0,
|
|
32
|
+
normalizedX: 0,
|
|
33
|
+
normalizedY: 0,
|
|
34
|
+
isDismissing: 0,
|
|
35
|
+
isDragging: 0,
|
|
36
|
+
},
|
|
37
|
+
bounds: {} as Record<string, BoundEntry>,
|
|
38
|
+
route: {} as RouteProp<ParamListBase>,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const useBuildScreenTransitionState = (
|
|
42
|
+
descriptor: NativeStackDescriptor | undefined,
|
|
43
|
+
): BuiltState | undefined => {
|
|
44
|
+
const key = descriptor?.route.key;
|
|
45
|
+
if (!key) return undefined;
|
|
46
|
+
const progress = Animations.getAnimation(key, "progress");
|
|
47
|
+
const closing = Animations.getAnimation(key, "closing");
|
|
48
|
+
const animating = Animations.getAnimation(key, "animating");
|
|
49
|
+
const gesture = Gestures.getRouteGestures(key);
|
|
50
|
+
const route = descriptor?.route;
|
|
51
|
+
|
|
52
|
+
return { progress, closing, animating, gesture, route };
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const unwrap = (
|
|
56
|
+
s: BuiltState | undefined,
|
|
57
|
+
key: string | undefined,
|
|
58
|
+
): ScreenTransitionState | undefined => {
|
|
59
|
+
"worklet";
|
|
60
|
+
return s && key
|
|
61
|
+
? {
|
|
62
|
+
progress: s.progress.value ?? 0,
|
|
63
|
+
closing: s.closing.value ?? 0,
|
|
64
|
+
animating: s.animating.value ?? 0,
|
|
65
|
+
gesture: {
|
|
66
|
+
x: s.gesture.x.value ?? 0,
|
|
67
|
+
y: s.gesture.y.value ?? 0,
|
|
68
|
+
normalizedX: s.gesture.normalizedX.value ?? 0,
|
|
69
|
+
normalizedY: s.gesture.normalizedY.value ?? 0,
|
|
70
|
+
isDismissing: s.gesture.isDismissing.value ?? 0,
|
|
71
|
+
isDragging: s.gesture.isDragging.value ?? 0,
|
|
72
|
+
},
|
|
73
|
+
bounds: Bounds.getBounds(key) ?? {},
|
|
74
|
+
route: s.route,
|
|
75
|
+
}
|
|
76
|
+
: undefined;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export function _useScreenAnimation() {
|
|
80
|
+
const {
|
|
81
|
+
current: currentDescriptor,
|
|
82
|
+
next: nextDescriptor,
|
|
83
|
+
previous: previousDescriptor,
|
|
84
|
+
} = useKeys();
|
|
85
|
+
|
|
86
|
+
const dimensions = useWindowDimensions();
|
|
87
|
+
|
|
88
|
+
const currentAnimation = useBuildScreenTransitionState(currentDescriptor);
|
|
89
|
+
|
|
90
|
+
const nextAnimation = useBuildScreenTransitionState(nextDescriptor);
|
|
91
|
+
const prevAnimation = useBuildScreenTransitionState(previousDescriptor);
|
|
92
|
+
|
|
93
|
+
const insets = useSafeAreaInsets();
|
|
94
|
+
|
|
95
|
+
const screenInterpolatorProps = useDerivedValue<ScreenInterpolationProps>(
|
|
96
|
+
() => {
|
|
97
|
+
"worklet";
|
|
98
|
+
|
|
99
|
+
const previous = unwrap(prevAnimation, previousDescriptor?.route.key);
|
|
100
|
+
const next = unwrap(nextAnimation, nextDescriptor?.route.key);
|
|
101
|
+
const current =
|
|
102
|
+
unwrap(currentAnimation, currentDescriptor?.route.key) ?? FALLBACK;
|
|
103
|
+
|
|
104
|
+
const progress = current.progress + (next?.progress ?? 0);
|
|
105
|
+
|
|
106
|
+
const focused = !next;
|
|
107
|
+
const activeBoundId = Bounds.getActiveBoundId() || "";
|
|
108
|
+
|
|
109
|
+
const bounds = buildBoundsAccessor({
|
|
110
|
+
activeBoundId,
|
|
111
|
+
current,
|
|
112
|
+
previous,
|
|
113
|
+
next,
|
|
114
|
+
progress,
|
|
115
|
+
dimensions,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
layouts: { screen: dimensions },
|
|
120
|
+
insets,
|
|
121
|
+
previous,
|
|
122
|
+
current,
|
|
123
|
+
next,
|
|
124
|
+
focused,
|
|
125
|
+
activeBoundId,
|
|
126
|
+
progress,
|
|
127
|
+
bounds,
|
|
128
|
+
} satisfies ScreenInterpolationProps;
|
|
129
|
+
},
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const screenStyleInterpolator =
|
|
133
|
+
nextDescriptor?.options.screenStyleInterpolator ||
|
|
134
|
+
currentDescriptor?.options.screenStyleInterpolator;
|
|
135
|
+
|
|
136
|
+
return { screenInterpolatorProps, screenStyleInterpolator };
|
|
137
|
+
}
|
|
138
|
+
export function useScreenAnimation() {
|
|
139
|
+
const { screenInterpolatorProps } = _useScreenAnimation();
|
|
140
|
+
|
|
141
|
+
return useDerivedValue(() => screenInterpolatorProps.value);
|
|
142
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import type { View } from "react-native";
|
|
3
|
+
import {
|
|
4
|
+
type AnimatedRef,
|
|
5
|
+
measure,
|
|
6
|
+
type StyleProps,
|
|
7
|
+
useSharedValue,
|
|
8
|
+
} from "react-native-reanimated";
|
|
9
|
+
import { useKeys } from "../../providers/keys";
|
|
10
|
+
import { Bounds } from "../../stores/bounds";
|
|
11
|
+
import { flattenStyle } from "../../utils/bounds/flatten-styles";
|
|
12
|
+
import { useScreenAnimation } from "../animation/use-screen-animation";
|
|
13
|
+
|
|
14
|
+
interface BoundMeasurerHookProps {
|
|
15
|
+
sharedBoundTag: string;
|
|
16
|
+
animatedRef: AnimatedRef<View>;
|
|
17
|
+
current: { route: { key: string } };
|
|
18
|
+
style: StyleProps;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const useBoundMeasurer = ({
|
|
22
|
+
sharedBoundTag,
|
|
23
|
+
animatedRef,
|
|
24
|
+
current,
|
|
25
|
+
style,
|
|
26
|
+
}: BoundMeasurerHookProps) => {
|
|
27
|
+
const { previous } = useKeys();
|
|
28
|
+
const interpolatorProps = useScreenAnimation();
|
|
29
|
+
const hasAlreadyMeasured = useSharedValue(false);
|
|
30
|
+
|
|
31
|
+
const measureAndSet = useCallback(() => {
|
|
32
|
+
"worklet";
|
|
33
|
+
if (!sharedBoundTag) return;
|
|
34
|
+
const measured = measure(animatedRef);
|
|
35
|
+
if (measured) {
|
|
36
|
+
Bounds.setBounds(
|
|
37
|
+
current.route.key,
|
|
38
|
+
sharedBoundTag,
|
|
39
|
+
measured,
|
|
40
|
+
flattenStyle(style),
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
}, [sharedBoundTag, animatedRef, current.route.key, style]);
|
|
44
|
+
|
|
45
|
+
const measureOnLayout = useCallback(() => {
|
|
46
|
+
"worklet";
|
|
47
|
+
if (!sharedBoundTag || hasAlreadyMeasured.value) return;
|
|
48
|
+
|
|
49
|
+
const previousRouteKey = previous?.route.key;
|
|
50
|
+
if (!previousRouteKey) return;
|
|
51
|
+
|
|
52
|
+
const previousBounds = Bounds.getBounds(previousRouteKey);
|
|
53
|
+
const hasPreviousBoundForTag = previousBounds[sharedBoundTag];
|
|
54
|
+
|
|
55
|
+
if (interpolatorProps.value.current.animating && hasPreviousBoundForTag) {
|
|
56
|
+
measureAndSet();
|
|
57
|
+
hasAlreadyMeasured.value = true;
|
|
58
|
+
}
|
|
59
|
+
}, [
|
|
60
|
+
measureAndSet,
|
|
61
|
+
interpolatorProps,
|
|
62
|
+
sharedBoundTag,
|
|
63
|
+
previous?.route.key,
|
|
64
|
+
hasAlreadyMeasured,
|
|
65
|
+
]);
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
measureAndSet,
|
|
69
|
+
measureOnLayout,
|
|
70
|
+
};
|
|
71
|
+
};
|