1 | import { Animated, Platform } from 'react-native';
|
2 |
|
3 | import type {
|
4 | StackCardInterpolatedStyle,
|
5 | StackCardInterpolationProps,
|
6 | } from '../types';
|
7 | import conditional from '../utils/conditional';
|
8 |
|
9 | const { add, multiply } = Animated;
|
10 |
|
11 |
|
12 |
|
13 |
|
14 | export function forHorizontalIOS({
|
15 | current,
|
16 | next,
|
17 | inverted,
|
18 | layouts: { screen },
|
19 | }: StackCardInterpolationProps): StackCardInterpolatedStyle {
|
20 | const translateFocused = multiply(
|
21 | current.progress.interpolate({
|
22 | inputRange: [0, 1],
|
23 | outputRange: [screen.width, 0],
|
24 | extrapolate: 'clamp',
|
25 | }),
|
26 | inverted
|
27 | );
|
28 |
|
29 | const translateUnfocused = next
|
30 | ? multiply(
|
31 | next.progress.interpolate({
|
32 | inputRange: [0, 1],
|
33 | outputRange: [0, screen.width * -0.3],
|
34 | extrapolate: 'clamp',
|
35 | }),
|
36 | inverted
|
37 | )
|
38 | : 0;
|
39 |
|
40 | const overlayOpacity = current.progress.interpolate({
|
41 | inputRange: [0, 1],
|
42 | outputRange: [0, 0.07],
|
43 | extrapolate: 'clamp',
|
44 | });
|
45 |
|
46 | const shadowOpacity = current.progress.interpolate({
|
47 | inputRange: [0, 1],
|
48 | outputRange: [0, 0.3],
|
49 | extrapolate: 'clamp',
|
50 | });
|
51 |
|
52 | return {
|
53 | cardStyle: {
|
54 | transform: [
|
55 |
|
56 | { translateX: translateFocused },
|
57 |
|
58 | { translateX: translateUnfocused },
|
59 | ],
|
60 | },
|
61 | overlayStyle: { opacity: overlayOpacity },
|
62 | shadowStyle: { shadowOpacity },
|
63 | };
|
64 | }
|
65 |
|
66 |
|
67 |
|
68 |
|
69 | export function forVerticalIOS({
|
70 | current,
|
71 | inverted,
|
72 | layouts: { screen },
|
73 | }: StackCardInterpolationProps): StackCardInterpolatedStyle {
|
74 | const translateY = multiply(
|
75 | current.progress.interpolate({
|
76 | inputRange: [0, 1],
|
77 | outputRange: [screen.height, 0],
|
78 | extrapolate: 'clamp',
|
79 | }),
|
80 | inverted
|
81 | );
|
82 |
|
83 | return {
|
84 | cardStyle: {
|
85 | transform: [{ translateY }],
|
86 | },
|
87 | };
|
88 | }
|
89 |
|
90 |
|
91 |
|
92 |
|
93 | export function forModalPresentationIOS({
|
94 | index,
|
95 | current,
|
96 | next,
|
97 | inverted,
|
98 | layouts: { screen },
|
99 | insets,
|
100 | }: StackCardInterpolationProps): StackCardInterpolatedStyle {
|
101 | const hasNotchIos =
|
102 | Platform.OS === 'ios' &&
|
103 | !Platform.isPad &&
|
104 | !Platform.isTV &&
|
105 | insets.top > 20;
|
106 | const isLandscape = screen.width > screen.height;
|
107 | const topOffset = isLandscape ? 0 : 10;
|
108 | const statusBarHeight = insets.top;
|
109 | const aspectRatio = screen.height / screen.width;
|
110 |
|
111 | const progress = add(
|
112 | current.progress.interpolate({
|
113 | inputRange: [0, 1],
|
114 | outputRange: [0, 1],
|
115 | extrapolate: 'clamp',
|
116 | }),
|
117 | next
|
118 | ? next.progress.interpolate({
|
119 | inputRange: [0, 1],
|
120 | outputRange: [0, 1],
|
121 | extrapolate: 'clamp',
|
122 | })
|
123 | : 0
|
124 | );
|
125 |
|
126 | const isFirst = index === 0;
|
127 |
|
128 | const translateY = multiply(
|
129 | progress.interpolate({
|
130 | inputRange: [0, 1, 2],
|
131 | outputRange: [
|
132 | screen.height,
|
133 | isFirst ? 0 : topOffset,
|
134 | (isFirst ? statusBarHeight : 0) - topOffset * aspectRatio,
|
135 | ],
|
136 | }),
|
137 | inverted
|
138 | );
|
139 |
|
140 | const overlayOpacity = progress.interpolate({
|
141 | inputRange: [0, 1, 1.0001, 2],
|
142 | outputRange: [0, 0.3, 1, 1],
|
143 | });
|
144 |
|
145 | const scale = isLandscape
|
146 | ? 1
|
147 | : progress.interpolate({
|
148 | inputRange: [0, 1, 2],
|
149 | outputRange: [
|
150 | 1,
|
151 | 1,
|
152 | screen.width ? 1 - (topOffset * 2) / screen.width : 1,
|
153 | ],
|
154 | });
|
155 |
|
156 | const borderRadius = isLandscape
|
157 | ? 0
|
158 | : isFirst
|
159 | ? progress.interpolate({
|
160 | inputRange: [0, 1, 1.0001, 2],
|
161 | outputRange: [0, 0, hasNotchIos ? 38 : 0, 10],
|
162 | })
|
163 | : 10;
|
164 |
|
165 | return {
|
166 | cardStyle: {
|
167 | overflow: 'hidden',
|
168 | borderTopLeftRadius: borderRadius,
|
169 | borderTopRightRadius: borderRadius,
|
170 |
|
171 |
|
172 | borderBottomLeftRadius: hasNotchIos ? borderRadius : 0,
|
173 | borderBottomRightRadius: hasNotchIos ? borderRadius : 0,
|
174 | marginTop: isFirst ? 0 : statusBarHeight,
|
175 | marginBottom: isFirst ? 0 : topOffset,
|
176 | transform: [{ translateY }, { scale }],
|
177 | },
|
178 | overlayStyle: { opacity: overlayOpacity },
|
179 | };
|
180 | }
|
181 |
|
182 |
|
183 |
|
184 |
|
185 | export function forFadeFromBottomAndroid({
|
186 | current,
|
187 | inverted,
|
188 | layouts: { screen },
|
189 | closing,
|
190 | }: StackCardInterpolationProps): StackCardInterpolatedStyle {
|
191 | const translateY = multiply(
|
192 | current.progress.interpolate({
|
193 | inputRange: [0, 1],
|
194 | outputRange: [screen.height * 0.08, 0],
|
195 | extrapolate: 'clamp',
|
196 | }),
|
197 | inverted
|
198 | );
|
199 |
|
200 | const opacity = conditional(
|
201 | closing,
|
202 | current.progress,
|
203 | current.progress.interpolate({
|
204 | inputRange: [0, 0.5, 0.9, 1],
|
205 | outputRange: [0, 0.25, 0.7, 1],
|
206 | extrapolate: 'clamp',
|
207 | })
|
208 | );
|
209 |
|
210 | return {
|
211 | cardStyle: {
|
212 | opacity,
|
213 | transform: [{ translateY }],
|
214 | },
|
215 | };
|
216 | }
|
217 |
|
218 |
|
219 |
|
220 |
|
221 | export function forRevealFromBottomAndroid({
|
222 | current,
|
223 | next,
|
224 | inverted,
|
225 | layouts: { screen },
|
226 | }: StackCardInterpolationProps): StackCardInterpolatedStyle {
|
227 | const containerTranslateY = multiply(
|
228 | current.progress.interpolate({
|
229 | inputRange: [0, 1],
|
230 | outputRange: [screen.height, 0],
|
231 | extrapolate: 'clamp',
|
232 | }),
|
233 | inverted
|
234 | );
|
235 |
|
236 | const cardTranslateYFocused = multiply(
|
237 | current.progress.interpolate({
|
238 | inputRange: [0, 1],
|
239 | outputRange: [screen.height * (95.9 / 100) * -1, 0],
|
240 | extrapolate: 'clamp',
|
241 | }),
|
242 | inverted
|
243 | );
|
244 |
|
245 | const cardTranslateYUnfocused = next
|
246 | ? multiply(
|
247 | next.progress.interpolate({
|
248 | inputRange: [0, 1],
|
249 | outputRange: [0, screen.height * (2 / 100) * -1],
|
250 | extrapolate: 'clamp',
|
251 | }),
|
252 | inverted
|
253 | )
|
254 | : 0;
|
255 |
|
256 | const overlayOpacity = current.progress.interpolate({
|
257 | inputRange: [0, 0.36, 1],
|
258 | outputRange: [0, 0.1, 0.1],
|
259 | extrapolate: 'clamp',
|
260 | });
|
261 |
|
262 | return {
|
263 | containerStyle: {
|
264 | overflow: 'hidden',
|
265 | transform: [{ translateY: containerTranslateY }],
|
266 | },
|
267 | cardStyle: {
|
268 | transform: [
|
269 | { translateY: cardTranslateYFocused },
|
270 | { translateY: cardTranslateYUnfocused },
|
271 | ],
|
272 | },
|
273 | overlayStyle: { opacity: overlayOpacity },
|
274 | };
|
275 | }
|
276 |
|
277 |
|
278 |
|
279 |
|
280 | export function forScaleFromCenterAndroid({
|
281 | current,
|
282 | next,
|
283 | closing,
|
284 | }: StackCardInterpolationProps): StackCardInterpolatedStyle {
|
285 | const progress = add(
|
286 | current.progress.interpolate({
|
287 | inputRange: [0, 1],
|
288 | outputRange: [0, 1],
|
289 | extrapolate: 'clamp',
|
290 | }),
|
291 | next
|
292 | ? next.progress.interpolate({
|
293 | inputRange: [0, 1],
|
294 | outputRange: [0, 1],
|
295 | extrapolate: 'clamp',
|
296 | })
|
297 | : 0
|
298 | );
|
299 |
|
300 | const opacity = progress.interpolate({
|
301 | inputRange: [0, 0.75, 0.875, 1, 1.0825, 1.2075, 2],
|
302 | outputRange: [0, 0, 1, 1, 1, 1, 0],
|
303 | });
|
304 |
|
305 | const scale = conditional(
|
306 | closing,
|
307 | current.progress.interpolate({
|
308 | inputRange: [0, 1],
|
309 | outputRange: [0.925, 1],
|
310 | extrapolate: 'clamp',
|
311 | }),
|
312 | progress.interpolate({
|
313 | inputRange: [0, 1, 2],
|
314 | outputRange: [0.85, 1, 1.075],
|
315 | })
|
316 | );
|
317 |
|
318 | return {
|
319 | cardStyle: {
|
320 | opacity,
|
321 | transform: [{ scale }],
|
322 | },
|
323 | };
|
324 | }
|
325 |
|
326 |
|
327 |
|
328 |
|
329 | export function forBottomSheetAndroid({
|
330 | current,
|
331 | inverted,
|
332 | layouts: { screen },
|
333 | closing,
|
334 | }: StackCardInterpolationProps): StackCardInterpolatedStyle {
|
335 | const translateY = multiply(
|
336 | current.progress.interpolate({
|
337 | inputRange: [0, 1],
|
338 | outputRange: [screen.height * 0.8, 0],
|
339 | extrapolate: 'clamp',
|
340 | }),
|
341 | inverted
|
342 | );
|
343 |
|
344 | const opacity = conditional(
|
345 | closing,
|
346 | current.progress,
|
347 | current.progress.interpolate({
|
348 | inputRange: [0, 1],
|
349 | outputRange: [0, 1],
|
350 | extrapolate: 'clamp',
|
351 | })
|
352 | );
|
353 |
|
354 | const overlayOpacity = current.progress.interpolate({
|
355 | inputRange: [0, 1],
|
356 | outputRange: [0, 0.3],
|
357 | extrapolate: 'clamp',
|
358 | });
|
359 |
|
360 | return {
|
361 | cardStyle: {
|
362 | opacity,
|
363 | transform: [{ translateY }],
|
364 | },
|
365 | overlayStyle: { opacity: overlayOpacity },
|
366 | };
|
367 | }
|
368 |
|
369 |
|
370 |
|
371 |
|
372 | export function forFadeFromCenter({
|
373 | current: { progress },
|
374 | }: StackCardInterpolationProps): StackCardInterpolatedStyle {
|
375 | return {
|
376 | cardStyle: {
|
377 | opacity: progress.interpolate({
|
378 | inputRange: [0, 0.5, 0.9, 1],
|
379 | outputRange: [0, 0.25, 0.7, 1],
|
380 | }),
|
381 | },
|
382 | overlayStyle: {
|
383 | opacity: progress.interpolate({
|
384 | inputRange: [0, 1],
|
385 | outputRange: [0, 0.5],
|
386 | extrapolate: 'clamp',
|
387 | }),
|
388 | },
|
389 | };
|
390 | }
|
391 |
|
392 | export function forNoAnimation(): StackCardInterpolatedStyle {
|
393 | return {};
|
394 | }
|