react-native-screen-transitions 1.0.1

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/README.md ADDED
@@ -0,0 +1,355 @@
1
+ # react-native-screen-transitions
2
+
3
+
4
+ | iOS | Android |
5
+ |---------|---------|
6
+ |<video src="https://github.com/user-attachments/assets/8a7b8006-f165-4a78-b0f9-c94cddd948b9" width="300" controls></video>|<video src="https://github.com/user-attachments/assets/ddebdaa8-a929-43ab-b857-08a00e142343" width="300" controls></video>|
7
+
8
+
9
+ **WIP**: This package is a work-in-progress. It provides customizable screen transition animations for React Native apps, primarily designed for use with `expo-router` and `react-navigation`. It supports gestures, predefined presets, and custom animations, making it easy to add polished transitions to your navigation flows.
10
+
11
+ This library is inspired by the transition system in `@react-navigation/stack` (not the native stack). If you're familiar with how transitions work there (e.g., using interpolators), you'll find this similar.
12
+
13
+
14
+
15
+
16
+ ## Features
17
+ - Predefined animation presets (e.g., SlideFromTop, ZoomIn, DraggableCard).
18
+ - Gesture support for interactive transitions (e.g., drag-to-dismiss).
19
+ - Animations using Reanimated.
20
+ - Easy integration with expo-router and react-navigation.
21
+
22
+ ## Compatibility
23
+ - **Platforms**: Currently tested on iOS and Android. Not tested or intended for web—web support is not a priority and may not work due to gesture and animation differences.
24
+ - **Dependencies**: Requires React Native, Reanimated, Gesture Handler, and either expo-router or react-navigation.
25
+
26
+ ## Installation
27
+ ```bash
28
+ npm install react-native-screen-transitions
29
+ # or
30
+ yarn add react-native-screen-transitions
31
+ # or
32
+ bun add react-native-screen-transitions
33
+ ```
34
+
35
+ ## Peer Dependencies
36
+ ```bash
37
+ npm install react-native-reanimated react-native-gesture-handler
38
+ ```
39
+
40
+ ## Usage
41
+
42
+ ### Integration with expo-router
43
+ In expo-router, you can define transitions in your root layout (`app/_layout.tsx`) using the `listeners` prop on `Stack.Screen`. Wrap your app in `GestureHandlerRootView` for gesture support.
44
+
45
+ ```tsx
46
+ // app/_layout.tsx
47
+ import { Stack } from "expo-router";
48
+ import { GestureHandlerRootView } from "react-native-gesture-handler";
49
+ import Transition from "react-native-screen-transitions";
50
+
51
+ export default function RootLayout() {
52
+ return (
53
+ <GestureHandlerRootView>
54
+ <Stack>
55
+ <Stack.Screen
56
+ name="index"
57
+ listeners={Transition.createConfig}
58
+ />
59
+ <Stack.Screen
60
+ name="a"
61
+ options={Transition.defaultScreenOptions()}
62
+ listeners={(l) =>
63
+ Transition.createConfig({
64
+ ...l,
65
+ ...Transition.presets.SlideFromTop(),
66
+ })
67
+ }
68
+ />
69
+ {/* Add more screens with presets */}
70
+ </Stack>
71
+ </GestureHandlerRootView>
72
+ );
73
+ }
74
+ ```
75
+
76
+ **Note**: `Transition.defaultScreenOptions()` returns the required screen options for animations to work properly. It sets `presentation: "containedTransparentModal"`, `headerShown: false`, and `animation: "none"` to ensure the library can control the transition animations.
77
+
78
+ For grouped routes with layouts (e.g., `app/group-a/_layout.tsx`), wrap the nested `Stack` in `Transition.View` to enable transitions:
79
+
80
+ ```tsx
81
+ // app/group-a/_layout.tsx
82
+ import { Stack } from "expo-router";
83
+ import Transition from "react-native-screen-transitions";
84
+
85
+ export default function GroupLayout() {
86
+ return (
87
+ <Transition.View>
88
+ <Stack>
89
+ <Stack.Screen name="a" />
90
+ {/* Nested screens */}
91
+ </Stack>
92
+ </Transition.View>
93
+ );
94
+ }
95
+ ```
96
+
97
+ ### Integration with react-navigation
98
+ For react-navigation, use `createNativeStackNavigator` and apply transitions via `listeners` and `options`.
99
+
100
+ ```tsx
101
+ // App.tsx
102
+ import { createStaticNavigation } from "@react-navigation/native";
103
+ import { createNativeStackNavigator } from "@react-navigation/native-stack";
104
+ import { GestureHandlerRootView } from "react-native-gesture-handler";
105
+ import Transition from "react-native-screen-transitions";
106
+ import { Home } from "./screens/Home"; // Your screens
107
+ import { ScreenA } from "./screens/ScreenA";
108
+
109
+ const RootStack = createNativeStackNavigator({
110
+ screens: {
111
+ Home: {
112
+ screen: Home,
113
+ listeners: Transition.createConfig,
114
+ },
115
+ ScreenA: {
116
+ screen: ScreenA,
117
+ options: Transition.defaultScreenOptions(),
118
+ listeners: (l) =>
119
+ Transition.createConfig({
120
+ ...l,
121
+ ...Transition.presets.SlideFromTop(),
122
+ }),
123
+ },
124
+ // Add more screens
125
+ },
126
+ });
127
+
128
+ const Navigation = createStaticNavigation(RootStack);
129
+
130
+ export default function App() {
131
+ return (
132
+ <GestureHandlerRootView>
133
+ <Navigation />
134
+ </GestureHandlerRootView>
135
+ );
136
+ }
137
+ ```
138
+
139
+ For nested navigators, wrap them in `Transition.View` similar to expo-router.
140
+
141
+ ### Predefined Presets
142
+ Use these out-of-the-box animations via `Transition.presets`:
143
+
144
+ - `SlideFromTop()`: Screen slides in from the top.
145
+ - `ZoomIn()`: Screen zooms in from the center.
146
+ - `SlideFromBottom()`: Screen slides in from the bottom.
147
+ - `DraggableCard()`: Interactive card-like drag gesture.
148
+ - `ElasticCard()`: Elastic bounce effect on drag.
149
+
150
+ Example:
151
+ ```tsx
152
+ listeners={(l) => Transition.createConfig({ ...l, ...Transition.presets.DraggableCard() })}
153
+ ```
154
+
155
+ ### Defining your own screen animations
156
+ There are two ways to define custom animations: at the navigator level using `screenStyleInterpolator` (recommended for animating both screens simultaneously), or at the screen level using `useScreenAnimation` hook.
157
+
158
+ #### Method 1: Navigator-Level Interpolator (Recommended)
159
+ Define a `screenStyleInterpolator` at the navigator level to animate both the entering and exiting screens simultaneously. This approach provides the most control.
160
+
161
+ ```tsx
162
+ // app/_layout.tsx
163
+ import { Stack } from "expo-router";
164
+ import { GestureHandlerRootView } from "react-native-gesture-handler";
165
+ import Transition from "react-native-screen-transitions";
166
+ import { interpolate, Easing } from "react-native-reanimated";
167
+
168
+ export default function RootLayout() {
169
+ return (
170
+ <GestureHandlerRootView>
171
+ <Stack>
172
+ <Stack.Screen
173
+ name="a"
174
+ listeners={Transition.createConfig} // Add blank config so system knows what to animate
175
+ />
176
+ <Stack.Screen
177
+ name="b"
178
+ options={Transition.defaultScreenOptions()}
179
+ listeners={(l) =>
180
+ Transition.createConfig({
181
+ ...l,
182
+ gestureDirection: "horizontal",
183
+ gestureEnabled: true,
184
+ gestureResponseDistance: 50,
185
+ gestureVelocityImpact: 0.3,
186
+ screenStyleInterpolator: ({
187
+ current,
188
+ next,
189
+ layouts: { screen: { width } },
190
+ }) => {
191
+ "worklet";
192
+
193
+ const progress = current.progress.value + (next?.progress.value || 0);
194
+
195
+ const translateX = interpolate(
196
+ progress,
197
+ [0, 1, 2],
198
+ [width, 0, width * -0.3],
199
+ "clamp"
200
+ );
201
+
202
+ return {
203
+ contentStyle: {
204
+ transform: [{ translateX }],
205
+ },
206
+ };
207
+ },
208
+ transitionSpec: {
209
+ open: {
210
+ easing: Easing.bezier(0.25, 0.1, 0.25, 1.0),
211
+ duration: 1000,
212
+ },
213
+ close: {
214
+ damping: 10,
215
+ mass: 0.5,
216
+ stiffness: 100,
217
+ },
218
+ },
219
+ })
220
+ }
221
+ />
222
+ </Stack>
223
+ </GestureHandlerRootView>
224
+ );
225
+ }
226
+ ```
227
+
228
+ When using `screenStyleInterpolator`, both screens must wrap their content in `Transition.View`:
229
+
230
+ ```tsx
231
+ // a.tsx
232
+ import Transition from 'react-native-screen-transitions';
233
+
234
+ export default function A() {
235
+ return (
236
+ <Transition.View>
237
+ {/* Your content */}
238
+ </Transition.View>
239
+ );
240
+ }
241
+
242
+ // b.tsx
243
+ import Transition from 'react-native-screen-transitions';
244
+
245
+ export default function B() {
246
+ return (
247
+ <Transition.View>
248
+ {/* Your content */}
249
+ </Transition.View>
250
+ );
251
+ }
252
+ ```
253
+
254
+ #### Method 2: Screen-Level Animations
255
+ Alternatively, define animations at the screen level using the `useScreenAnimation` hook. This is useful for screen-specific effects or when you don't need to animate both screens.
256
+
257
+ ```tsx
258
+ // app/_layout.tsx
259
+ <Stack.Screen
260
+ name="a"
261
+ listeners={Transition.createConfig}
262
+ />
263
+ <Stack.Screen
264
+ name="b"
265
+ options={Transition.defaultScreenOptions()}
266
+ listeners={(l) =>
267
+ Transition.createConfig({
268
+ ...l,
269
+ transitionSpec: {
270
+ open: {
271
+ easing: Easing.bezier(0.25, 0.1, 0.25, 1.0),
272
+ duration: 1000,
273
+ },
274
+ close: {
275
+ damping: 10,
276
+ mass: 0.5,
277
+ stiffness: 100,
278
+ },
279
+ },
280
+ })
281
+ }
282
+ />
283
+ ```
284
+
285
+ ```tsx
286
+ // a.tsx (previous screen)
287
+ import { useScreenAnimation } from 'react-native-screen-transitions';
288
+ import Animated, { useAnimatedStyle, interpolate } from 'react-native-reanimated';
289
+
290
+ export default function A() {
291
+ const { next, layouts: { screen: { width } } } = useScreenAnimation();
292
+
293
+ const animatedStyle = useAnimatedStyle(() => {
294
+ // Unfocusing animation - screen slides left when next screen enters
295
+ const translateX = interpolate(next?.progress.value || 0, [0, 1], [0, width * -0.3]);
296
+ return {
297
+ transform: [{ translateX }],
298
+ };
299
+ });
300
+
301
+ return (
302
+ <Animated.View style={animatedStyle}>
303
+ {/* Your content */}
304
+ </Animated.View>
305
+ );
306
+ }
307
+
308
+ // b.tsx (entering screen)
309
+ import { useScreenAnimation } from 'react-native-screen-transitions';
310
+ import Animated, { useAnimatedStyle, interpolate } from 'react-native-reanimated';
311
+
312
+ export default function B() {
313
+ const { current, layouts: { screen: { width } } } = useScreenAnimation();
314
+
315
+ const animatedStyle = useAnimatedStyle(() => {
316
+ // Focusing animation - screen slides in from right
317
+ const translateX = interpolate(current.progress.value, [0, 1], [width, 0]);
318
+ return {
319
+ transform: [{ translateX }],
320
+ };
321
+ });
322
+
323
+ return (
324
+ <Animated.View style={[{ flex: 1 }, animatedStyle]}>
325
+ {/* Your content */}
326
+ </Animated.View>
327
+ );
328
+ }
329
+
330
+
331
+ ```
332
+
333
+
334
+ ### Known Issues and Roadmap
335
+ This is a WIP package, so expect improvements. Current known issues:
336
+ - **Gestures with ScrollViews**: Gestures can be wonky when combined with scrollable content. For example, if a screen defines vertical dismissal gestures and contains a vertical `ScrollView`, the gesture may not trigger reliably (conflicts with scroll handling).
337
+ - **Web Support**: Not intended or tested for web—focus is on mobile (iOS/Android). Web may have issues with gestures and animations.
338
+ - **Other**: Limited testing in complex nested navigators; potential edge cases with rapid transitions.
339
+
340
+
341
+ ### Note:
342
+
343
+ This package is something I'll work on in my freetime. However, you can see the roadmap I plan on following in the coming future.
344
+
345
+ Roadmap:
346
+ - Fix gesture conflicts with ScrollViews (e.g., better gesture priority handling).
347
+ - Add more presets and customization options.
348
+ - Improve documentation and examples.
349
+ - Potential web support if demand arises.
350
+ - Testing for more edge cases (e.g., modals, tabs).
351
+
352
+ See the examples in `/examples/expo-router-example` and `/examples/react-nav-example` for full demos.
353
+
354
+ ## License
355
+ MIT