react-native-screen-transitions 3.2.0-beta.2 → 3.2.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/README.md +346 -620
- package/lib/commonjs/blank-stack/components/stack-view.js +0 -2
- package/lib/commonjs/blank-stack/components/stack-view.js.map +1 -1
- package/lib/commonjs/blank-stack/components/stack-view.native.js +0 -2
- package/lib/commonjs/blank-stack/components/stack-view.native.js.map +1 -1
- package/lib/commonjs/component-stack/components/stack-view.js +16 -20
- package/lib/commonjs/component-stack/components/stack-view.js.map +1 -1
- package/lib/commonjs/native-stack/views/NativeStackView.native.js +3 -3
- package/lib/commonjs/native-stack/views/NativeStackView.native.js.map +1 -1
- package/lib/commonjs/shared/components/overlay/variations/float-overlay.js +1 -6
- package/lib/commonjs/shared/components/overlay/variations/float-overlay.js.map +1 -1
- package/lib/commonjs/shared/components/overlay/variations/overlay-host.js +16 -64
- package/lib/commonjs/shared/components/overlay/variations/overlay-host.js.map +1 -1
- package/lib/commonjs/shared/components/overlay/variations/screen-overlay.js +5 -8
- package/lib/commonjs/shared/components/overlay/variations/screen-overlay.js.map +1 -1
- package/lib/commonjs/shared/components/screen-lifecycle.js +29 -0
- package/lib/commonjs/shared/components/screen-lifecycle.js.map +1 -0
- package/lib/commonjs/shared/constants.js +4 -2
- package/lib/commonjs/shared/constants.js.map +1 -1
- package/lib/commonjs/shared/hooks/animation/use-screen-animation.js +3 -0
- package/lib/commonjs/shared/hooks/animation/use-screen-animation.js.map +1 -1
- package/lib/commonjs/shared/hooks/gestures/use-build-gestures.js.map +1 -1
- package/lib/commonjs/shared/hooks/lifecycle/use-close-transition.js +127 -0
- package/lib/commonjs/shared/hooks/lifecycle/use-close-transition.js.map +1 -0
- package/lib/commonjs/shared/hooks/lifecycle/use-open-transition.js +35 -0
- package/lib/commonjs/shared/hooks/lifecycle/use-open-transition.js.map +1 -0
- package/lib/commonjs/shared/hooks/lifecycle/use-screen-events.js +58 -0
- package/lib/commonjs/shared/hooks/lifecycle/use-screen-events.js.map +1 -0
- package/lib/commonjs/shared/hooks/navigation/use-history.js +24 -0
- package/lib/commonjs/shared/hooks/navigation/use-history.js.map +1 -0
- package/lib/commonjs/shared/hooks/navigation/use-screen-state.js +45 -0
- package/lib/commonjs/shared/hooks/navigation/use-screen-state.js.map +1 -0
- package/lib/commonjs/shared/hooks/use-stable-callback-value.js +0 -2
- package/lib/commonjs/shared/hooks/use-stable-callback-value.js.map +1 -1
- package/lib/commonjs/shared/index.js +14 -0
- package/lib/commonjs/shared/index.js.map +1 -1
- package/lib/commonjs/shared/providers/screen/keys.provider.js +0 -6
- package/lib/commonjs/shared/providers/screen/keys.provider.js.map +1 -1
- package/lib/commonjs/shared/providers/screen/screen-composer.js +9 -7
- package/lib/commonjs/shared/providers/screen/screen-composer.js.map +1 -1
- package/lib/commonjs/shared/providers/screen/styles.provider.js +1 -1
- package/lib/commonjs/shared/providers/screen/styles.provider.js.map +1 -1
- package/lib/commonjs/shared/providers/stack/direct.provider.js +9 -0
- package/lib/commonjs/shared/providers/stack/direct.provider.js.map +1 -1
- package/lib/commonjs/shared/providers/stack/managed.provider.js +9 -0
- package/lib/commonjs/shared/providers/stack/managed.provider.js.map +1 -1
- package/lib/commonjs/shared/stores/animation.store.js +3 -13
- package/lib/commonjs/shared/stores/animation.store.js.map +1 -1
- package/lib/commonjs/shared/stores/history.store.js +185 -0
- package/lib/commonjs/shared/stores/history.store.js.map +1 -0
- package/lib/commonjs/shared/types/index.js +3 -3
- package/lib/commonjs/shared/types/index.js.map +1 -1
- package/lib/commonjs/shared/types/stack.types.js +4 -0
- package/lib/commonjs/shared/types/stack.types.js.map +1 -1
- package/lib/commonjs/shared/utils/animation/start-screen-transition.js +10 -6
- package/lib/commonjs/shared/utils/animation/start-screen-transition.js.map +1 -1
- package/lib/commonjs/shared/utils/bounds/index.js +19 -4
- package/lib/commonjs/shared/utils/bounds/index.js.map +1 -1
- package/lib/module/blank-stack/components/stack-view.js +0 -2
- package/lib/module/blank-stack/components/stack-view.js.map +1 -1
- package/lib/module/blank-stack/components/stack-view.native.js +0 -2
- package/lib/module/blank-stack/components/stack-view.native.js.map +1 -1
- package/lib/module/component-stack/components/stack-view.js +16 -20
- package/lib/module/component-stack/components/stack-view.js.map +1 -1
- package/lib/module/native-stack/views/NativeStackView.native.js +3 -3
- package/lib/module/native-stack/views/NativeStackView.native.js.map +1 -1
- package/lib/module/shared/components/overlay/variations/float-overlay.js +1 -6
- package/lib/module/shared/components/overlay/variations/float-overlay.js.map +1 -1
- package/lib/module/shared/components/overlay/variations/overlay-host.js +16 -64
- package/lib/module/shared/components/overlay/variations/overlay-host.js.map +1 -1
- package/lib/module/shared/components/overlay/variations/screen-overlay.js +5 -8
- package/lib/module/shared/components/overlay/variations/screen-overlay.js.map +1 -1
- package/lib/module/shared/components/screen-lifecycle.js +24 -0
- package/lib/module/shared/components/screen-lifecycle.js.map +1 -0
- package/lib/module/shared/constants.js +3 -1
- package/lib/module/shared/constants.js.map +1 -1
- package/lib/module/shared/hooks/animation/use-screen-animation.js +3 -0
- package/lib/module/shared/hooks/animation/use-screen-animation.js.map +1 -1
- package/lib/module/shared/hooks/gestures/use-build-gestures.js.map +1 -1
- package/lib/module/shared/hooks/lifecycle/use-close-transition.js +122 -0
- package/lib/module/shared/hooks/lifecycle/use-close-transition.js.map +1 -0
- package/lib/module/shared/hooks/lifecycle/use-open-transition.js +32 -0
- package/lib/module/shared/hooks/lifecycle/use-open-transition.js.map +1 -0
- package/lib/module/shared/hooks/lifecycle/use-screen-events.js +54 -0
- package/lib/module/shared/hooks/lifecycle/use-screen-events.js.map +1 -0
- package/lib/module/shared/hooks/navigation/use-history.js +20 -0
- package/lib/module/shared/hooks/navigation/use-history.js.map +1 -0
- package/lib/module/shared/hooks/navigation/use-screen-state.js +41 -0
- package/lib/module/shared/hooks/navigation/use-screen-state.js.map +1 -0
- package/lib/module/shared/hooks/use-stable-callback-value.js +0 -3
- package/lib/module/shared/hooks/use-stable-callback-value.js.map +1 -1
- package/lib/module/shared/index.js +2 -0
- package/lib/module/shared/index.js.map +1 -1
- package/lib/module/shared/providers/screen/keys.provider.js +0 -6
- package/lib/module/shared/providers/screen/keys.provider.js.map +1 -1
- package/lib/module/shared/providers/screen/screen-composer.js +9 -7
- package/lib/module/shared/providers/screen/screen-composer.js.map +1 -1
- package/lib/module/shared/providers/screen/styles.provider.js +1 -1
- package/lib/module/shared/providers/screen/styles.provider.js.map +1 -1
- package/lib/module/shared/providers/stack/direct.provider.js +10 -1
- package/lib/module/shared/providers/stack/direct.provider.js.map +1 -1
- package/lib/module/shared/providers/stack/managed.provider.js +10 -1
- package/lib/module/shared/providers/stack/managed.provider.js.map +1 -1
- package/lib/module/shared/stores/animation.store.js +4 -14
- package/lib/module/shared/stores/animation.store.js.map +1 -1
- package/lib/module/shared/stores/history.store.js +181 -0
- package/lib/module/shared/stores/history.store.js.map +1 -0
- package/lib/module/shared/types/index.js +1 -1
- package/lib/module/shared/types/index.js.map +1 -1
- package/lib/module/shared/types/stack.types.js +5 -0
- package/lib/module/shared/types/stack.types.js.map +1 -1
- package/lib/module/shared/utils/animation/start-screen-transition.js +6 -2
- package/lib/module/shared/utils/animation/start-screen-transition.js.map +1 -1
- package/lib/module/shared/utils/bounds/index.js +19 -4
- package/lib/module/shared/utils/bounds/index.js.map +1 -1
- package/lib/typescript/blank-stack/components/stack-view.d.ts.map +1 -1
- package/lib/typescript/blank-stack/components/stack-view.native.d.ts.map +1 -1
- package/lib/typescript/blank-stack/types.d.ts +1 -8
- package/lib/typescript/blank-stack/types.d.ts.map +1 -1
- package/lib/typescript/component-stack/components/stack-view.d.ts.map +1 -1
- package/lib/typescript/component-stack/types.d.ts +1 -8
- package/lib/typescript/component-stack/types.d.ts.map +1 -1
- package/lib/typescript/native-stack/types.d.ts +2 -3
- package/lib/typescript/native-stack/types.d.ts.map +1 -1
- package/lib/typescript/native-stack/views/NativeStackView.native.d.ts.map +1 -1
- package/lib/typescript/shared/components/overlay/variations/float-overlay.d.ts.map +1 -1
- package/lib/typescript/shared/components/overlay/variations/overlay-host.d.ts +1 -6
- package/lib/typescript/shared/components/overlay/variations/overlay-host.d.ts.map +1 -1
- package/lib/typescript/shared/components/overlay/variations/screen-overlay.d.ts +4 -0
- package/lib/typescript/shared/components/overlay/variations/screen-overlay.d.ts.map +1 -1
- package/lib/typescript/shared/components/screen-lifecycle.d.ts +12 -0
- package/lib/typescript/shared/components/screen-lifecycle.d.ts.map +1 -0
- package/lib/typescript/shared/constants.d.ts +2 -1
- package/lib/typescript/shared/constants.d.ts.map +1 -1
- package/lib/typescript/shared/hooks/animation/use-screen-animation.d.ts.map +1 -1
- package/lib/typescript/shared/hooks/gestures/use-build-gestures.d.ts.map +1 -1
- package/lib/typescript/shared/hooks/lifecycle/use-close-transition.d.ts +13 -0
- package/lib/typescript/shared/hooks/lifecycle/use-close-transition.d.ts.map +1 -0
- package/lib/typescript/shared/hooks/lifecycle/use-open-transition.d.ts +11 -0
- package/lib/typescript/shared/hooks/lifecycle/use-open-transition.d.ts.map +1 -0
- package/lib/typescript/shared/hooks/lifecycle/use-screen-events.d.ts +7 -0
- package/lib/typescript/shared/hooks/lifecycle/use-screen-events.d.ts.map +1 -0
- package/lib/typescript/shared/hooks/navigation/use-history.d.ts +37 -0
- package/lib/typescript/shared/hooks/navigation/use-history.d.ts.map +1 -0
- package/lib/typescript/shared/hooks/navigation/use-screen-state.d.ts +40 -0
- package/lib/typescript/shared/hooks/navigation/use-screen-state.d.ts.map +1 -0
- package/lib/typescript/shared/hooks/use-stable-callback-value.d.ts.map +1 -1
- package/lib/typescript/shared/index.d.ts +22 -20
- package/lib/typescript/shared/index.d.ts.map +1 -1
- package/lib/typescript/shared/providers/screen/keys.provider.d.ts +2 -9
- package/lib/typescript/shared/providers/screen/keys.provider.d.ts.map +1 -1
- package/lib/typescript/shared/providers/screen/screen-composer.d.ts +1 -3
- package/lib/typescript/shared/providers/screen/screen-composer.d.ts.map +1 -1
- package/lib/typescript/shared/providers/stack/direct.provider.d.ts.map +1 -1
- package/lib/typescript/shared/providers/stack/managed.provider.d.ts.map +1 -1
- package/lib/typescript/shared/stores/animation.store.d.ts +3 -4
- package/lib/typescript/shared/stores/animation.store.d.ts.map +1 -1
- package/lib/typescript/shared/stores/history.store.d.ts +82 -0
- package/lib/typescript/shared/stores/history.store.d.ts.map +1 -0
- package/lib/typescript/shared/types/animation.types.d.ts +10 -6
- package/lib/typescript/shared/types/animation.types.d.ts.map +1 -1
- package/lib/typescript/shared/types/bounds.types.d.ts +1 -1
- package/lib/typescript/shared/types/bounds.types.d.ts.map +1 -1
- package/lib/typescript/shared/types/index.d.ts +2 -2
- package/lib/typescript/shared/types/index.d.ts.map +1 -1
- package/lib/typescript/shared/types/overlay.types.d.ts +21 -0
- package/lib/typescript/shared/types/overlay.types.d.ts.map +1 -1
- package/lib/typescript/shared/types/screen.types.d.ts +5 -4
- package/lib/typescript/shared/types/screen.types.d.ts.map +1 -1
- package/lib/typescript/shared/types/stack.types.d.ts +10 -0
- package/lib/typescript/shared/types/stack.types.d.ts.map +1 -1
- package/lib/typescript/shared/utils/animation/start-screen-transition.d.ts.map +1 -1
- package/lib/typescript/shared/utils/bounds/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/blank-stack/components/stack-view.native.tsx +0 -2
- package/src/blank-stack/components/stack-view.tsx +0 -2
- package/src/blank-stack/types.ts +0 -10
- package/src/component-stack/components/stack-view.tsx +19 -23
- package/src/component-stack/types.ts +0 -10
- package/src/native-stack/types.ts +2 -3
- package/src/native-stack/views/NativeStackView.native.tsx +2 -3
- package/src/shared/__tests__/history.store.test.ts +550 -0
- package/src/shared/components/overlay/variations/float-overlay.tsx +2 -8
- package/src/shared/components/overlay/variations/overlay-host.tsx +18 -84
- package/src/shared/components/overlay/variations/screen-overlay.tsx +6 -15
- package/src/shared/components/screen-lifecycle.tsx +32 -0
- package/src/shared/constants.ts +4 -1
- package/src/shared/hooks/animation/use-screen-animation.tsx +7 -3
- package/src/shared/hooks/gestures/use-build-gestures.tsx +0 -2
- package/src/shared/hooks/lifecycle/use-close-transition.ts +147 -0
- package/src/shared/hooks/lifecycle/use-open-transition.ts +30 -0
- package/src/shared/hooks/lifecycle/use-screen-events.ts +62 -0
- package/src/shared/hooks/navigation/use-history.ts +63 -0
- package/src/shared/hooks/navigation/use-screen-state.tsx +99 -0
- package/src/shared/hooks/use-stable-callback-value.ts +0 -1
- package/src/shared/index.ts +5 -0
- package/src/shared/providers/screen/keys.provider.tsx +1 -25
- package/src/shared/providers/screen/screen-composer.tsx +7 -13
- package/src/shared/providers/screen/styles.provider.tsx +1 -1
- package/src/shared/providers/stack/direct.provider.tsx +11 -1
- package/src/shared/providers/stack/managed.provider.tsx +11 -1
- package/src/shared/stores/animation.store.ts +6 -20
- package/src/shared/stores/history.store.ts +201 -0
- package/src/shared/types/animation.types.ts +11 -6
- package/src/shared/types/bounds.types.ts +1 -0
- package/src/shared/types/index.ts +2 -7
- package/src/shared/types/overlay.types.ts +23 -0
- package/src/shared/types/screen.types.ts +5 -4
- package/src/shared/types/stack.types.ts +17 -1
- package/src/shared/utils/animation/start-screen-transition.ts +5 -2
- package/src/shared/utils/bounds/index.ts +35 -4
- package/lib/commonjs/native-stack/controllers/native-stack-lifecycle.js +0 -90
- package/lib/commonjs/native-stack/controllers/native-stack-lifecycle.js.map +0 -1
- package/lib/commonjs/shared/controller/managed-lifecycle.js +0 -78
- package/lib/commonjs/shared/controller/managed-lifecycle.js.map +0 -1
- package/lib/commonjs/shared/types/state.types.js +0 -9
- package/lib/commonjs/shared/types/state.types.js.map +0 -1
- package/lib/commonjs/shared/utils/read-shared-value.js +0 -17
- package/lib/commonjs/shared/utils/read-shared-value.js.map +0 -1
- package/lib/module/native-stack/controllers/native-stack-lifecycle.js +0 -83
- package/lib/module/native-stack/controllers/native-stack-lifecycle.js.map +0 -1
- package/lib/module/shared/controller/managed-lifecycle.js +0 -72
- package/lib/module/shared/controller/managed-lifecycle.js.map +0 -1
- package/lib/module/shared/types/state.types.js +0 -5
- package/lib/module/shared/types/state.types.js.map +0 -1
- package/lib/module/shared/utils/read-shared-value.js +0 -14
- package/lib/module/shared/utils/read-shared-value.js.map +0 -1
- package/lib/typescript/native-stack/controllers/native-stack-lifecycle.d.ts +0 -8
- package/lib/typescript/native-stack/controllers/native-stack-lifecycle.d.ts.map +0 -1
- package/lib/typescript/shared/controller/managed-lifecycle.d.ts +0 -9
- package/lib/typescript/shared/controller/managed-lifecycle.d.ts.map +0 -1
- package/lib/typescript/shared/types/state.types.d.ts +0 -3
- package/lib/typescript/shared/types/state.types.d.ts.map +0 -1
- package/lib/typescript/shared/utils/read-shared-value.d.ts +0 -7
- package/lib/typescript/shared/utils/read-shared-value.d.ts.map +0 -1
- package/src/native-stack/controllers/native-stack-lifecycle.tsx +0 -96
- package/src/shared/controller/managed-lifecycle.tsx +0 -73
- package/src/shared/types/state.types.ts +0 -2
- package/src/shared/utils/read-shared-value.ts +0 -15
package/README.md
CHANGED
|
@@ -9,10 +9,9 @@ Customizable screen transitions for React Native. Build gesture-driven, shared e
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
11
11
|
- **Full Animation Control** – Define exactly how screens enter, exit, and respond to gestures
|
|
12
|
-
- **Shared Elements** –
|
|
13
|
-
- **Gesture Support** – Swipe-to-dismiss with edge or full-screen activation
|
|
14
|
-
- **
|
|
15
|
-
- **Stack Progress** – Track animation progress across the entire stack, not just adjacent screens
|
|
12
|
+
- **Shared Elements** – Smooth transitions between screens using the Bounds API
|
|
13
|
+
- **Gesture Support** – Swipe-to-dismiss with edge or full-screen activation
|
|
14
|
+
- **Stack Progress** – Track animation progress across the entire stack
|
|
16
15
|
- **Ready-Made Presets** – Instagram, Apple Music, X (Twitter) style transitions included
|
|
17
16
|
|
|
18
17
|
## Installation
|
|
@@ -34,79 +33,7 @@ npm install react-native-reanimated react-native-gesture-handler \
|
|
|
34
33
|
|
|
35
34
|
## Quick Start
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
| Stack | Description |
|
|
40
|
-
| ----------------------------- | --------------------------------------------------------------------------------- |
|
|
41
|
-
| **Blank Stack** (recommended) | Pure JavaScript stack with full control over transitions, overlays, and gestures. |
|
|
42
|
-
| **Native Stack** | Extends `@react-navigation/native-stack`. Full overlay support, native primitives.|
|
|
43
|
-
| **Component Stack** | Standalone navigator without React Navigation. For internal/embedded navigation. |
|
|
44
|
-
|
|
45
|
-
### Choosing a Stack
|
|
46
|
-
|
|
47
|
-
**Blank Stack** is feature-rich and recommended for most use cases:
|
|
48
|
-
|
|
49
|
-
- Full overlay system (float and screen modes)
|
|
50
|
-
- Stack progress tracking across the entire stack
|
|
51
|
-
- No delayed touch events on exiting screens
|
|
52
|
-
|
|
53
|
-
However, it's still a JavaScript implementation. While optimized to be as fast as possible (using `react-native-screens` under the hood, with animations and gesture logic running on the UI thread), heavy usage may not match native performance.
|
|
54
|
-
|
|
55
|
-
**Native Stack** uses native navigation primitives with some trade-offs:
|
|
56
|
-
|
|
57
|
-
- Full overlay system support (float and screen modes)
|
|
58
|
-
- Relies on `beforeRemove` listeners to intercept navigation
|
|
59
|
-
- Uses transparent modal presentation which can cause delayed touch events
|
|
60
|
-
- Some edge cases with rapid navigation
|
|
61
|
-
|
|
62
|
-
Choose Native Stack if you need native performance with overlay support.
|
|
63
|
-
|
|
64
|
-
**Component Stack** is a standalone navigator that doesn't integrate with React Navigation:
|
|
65
|
-
|
|
66
|
-
- No integration with React Navigation ecosystem (no linking, no deep linking)
|
|
67
|
-
- Ideal for embedded navigation within a screen (e.g., onboarding flows, wizards)
|
|
68
|
-
- Lightweight, component-based API
|
|
69
|
-
- Full transition and gesture support
|
|
70
|
-
|
|
71
|
-
### Blank Stack Philosophy
|
|
72
|
-
|
|
73
|
-
The Blank Stack is intentionally **blank** - transparent screens with no default animations. Unlike platform navigators that impose iOS or Android-style transitions, the Blank Stack gives you a clean slate.
|
|
74
|
-
|
|
75
|
-
**Why no defaults?**
|
|
76
|
-
|
|
77
|
-
- **Full creative control** – You define exactly how screens appear, not the OS
|
|
78
|
-
- **Consistency across platforms** – Same animation on iOS and Android
|
|
79
|
-
- **No fighting the framework** – No need to override or disable built-in behaviors
|
|
80
|
-
|
|
81
|
-
Every screen starts invisible and static. You bring it to life with your own `screenStyleInterpolator`. This encourages intentional, custom transitions rather than settling for platform defaults.
|
|
82
|
-
|
|
83
|
-
Under the hood, the Blank Stack uses `react-native-screens` for native-level performance. All animation and gesture logic runs on the UI thread via Reanimated worklets.
|
|
84
|
-
|
|
85
|
-
```tsx
|
|
86
|
-
// A screen with no options = invisible, no animation
|
|
87
|
-
<Stack.Screen name="Detail" component={DetailScreen} />
|
|
88
|
-
|
|
89
|
-
// Add your own transition
|
|
90
|
-
<Stack.Screen
|
|
91
|
-
name="Detail"
|
|
92
|
-
component={DetailScreen}
|
|
93
|
-
options={{
|
|
94
|
-
screenStyleInterpolator: ({ progress, layouts }) => {
|
|
95
|
-
"worklet";
|
|
96
|
-
return {
|
|
97
|
-
contentStyle: {
|
|
98
|
-
opacity: progress,
|
|
99
|
-
transform: [
|
|
100
|
-
{ translateY: interpolate(progress, [0, 1], [layouts.screen.height, 0]) }
|
|
101
|
-
],
|
|
102
|
-
},
|
|
103
|
-
};
|
|
104
|
-
},
|
|
105
|
-
}}
|
|
106
|
-
/>
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
### Blank Stack Setup
|
|
36
|
+
### 1. Create a Stack
|
|
110
37
|
|
|
111
38
|
```tsx
|
|
112
39
|
import { createBlankStackNavigator } from "react-native-screen-transitions/blank-stack";
|
|
@@ -130,17 +57,12 @@ function App() {
|
|
|
130
57
|
}
|
|
131
58
|
```
|
|
132
59
|
|
|
133
|
-
###
|
|
60
|
+
### 2. With Expo Router
|
|
134
61
|
|
|
135
62
|
```tsx
|
|
136
|
-
import type {
|
|
137
|
-
ParamListBase,
|
|
138
|
-
StackNavigationState,
|
|
139
|
-
} from "@react-navigation/native";
|
|
140
63
|
import { withLayoutContext } from "expo-router";
|
|
141
64
|
import {
|
|
142
65
|
createBlankStackNavigator,
|
|
143
|
-
type BlankStackNavigationEventMap,
|
|
144
66
|
type BlankStackNavigationOptions,
|
|
145
67
|
} from "react-native-screen-transitions/blank-stack";
|
|
146
68
|
|
|
@@ -148,9 +70,7 @@ const { Navigator } = createBlankStackNavigator();
|
|
|
148
70
|
|
|
149
71
|
export const Stack = withLayoutContext<
|
|
150
72
|
BlankStackNavigationOptions,
|
|
151
|
-
typeof Navigator
|
|
152
|
-
StackNavigationState<ParamListBase>,
|
|
153
|
-
BlankStackNavigationEventMap
|
|
73
|
+
typeof Navigator
|
|
154
74
|
>(Navigator);
|
|
155
75
|
```
|
|
156
76
|
|
|
@@ -158,7 +78,7 @@ export const Stack = withLayoutContext<
|
|
|
158
78
|
|
|
159
79
|
## Presets
|
|
160
80
|
|
|
161
|
-
|
|
81
|
+
Use built-in presets for common transitions:
|
|
162
82
|
|
|
163
83
|
```tsx
|
|
164
84
|
<Stack.Screen
|
|
@@ -169,496 +89,486 @@ Built-in animation presets you can spread into screen options:
|
|
|
169
89
|
/>
|
|
170
90
|
```
|
|
171
91
|
|
|
172
|
-
| Preset | Description
|
|
173
|
-
| -------------------------------------- |
|
|
174
|
-
| `SlideFromTop()` | Slides in from top
|
|
175
|
-
| `SlideFromBottom()` | Slides in from bottom
|
|
176
|
-
| `ZoomIn()` | Scales in with fade
|
|
177
|
-
| `DraggableCard()` | Multi-directional drag with
|
|
178
|
-
| `ElasticCard()` | Elastic drag with overlay
|
|
179
|
-
| `SharedIGImage({ sharedBoundTag })` | Instagram-style shared image
|
|
180
|
-
| `SharedAppleMusic({ sharedBoundTag })` | Apple Music-style shared element
|
|
181
|
-
| `SharedXImage({ sharedBoundTag })` | X (Twitter)-style image transition
|
|
92
|
+
| Preset | Description |
|
|
93
|
+
| -------------------------------------- | --------------------------------------- |
|
|
94
|
+
| `SlideFromTop()` | Slides in from top |
|
|
95
|
+
| `SlideFromBottom()` | Slides in from bottom (modal-style) |
|
|
96
|
+
| `ZoomIn()` | Scales in with fade |
|
|
97
|
+
| `DraggableCard()` | Multi-directional drag with scaling |
|
|
98
|
+
| `ElasticCard()` | Elastic drag with overlay |
|
|
99
|
+
| `SharedIGImage({ sharedBoundTag })` | Instagram-style shared image |
|
|
100
|
+
| `SharedAppleMusic({ sharedBoundTag })` | Apple Music-style shared element |
|
|
101
|
+
| `SharedXImage({ sharedBoundTag })` | X (Twitter)-style image transition |
|
|
182
102
|
|
|
183
103
|
---
|
|
184
104
|
|
|
185
105
|
## Custom Animations
|
|
186
106
|
|
|
187
|
-
###
|
|
107
|
+
### The Basics
|
|
188
108
|
|
|
189
|
-
|
|
109
|
+
Every screen has a `progress` value that goes from 0 → 1 → 2:
|
|
190
110
|
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
<Stack.Screen
|
|
195
|
-
name="Detail"
|
|
196
|
-
options={{
|
|
197
|
-
screenStyleInterpolator: ({ progress, layouts: { screen } }) => {
|
|
198
|
-
"worklet";
|
|
199
|
-
|
|
200
|
-
const translateX = interpolate(
|
|
201
|
-
progress,
|
|
202
|
-
[0, 1, 2],
|
|
203
|
-
[screen.width, 0, -screen.width]
|
|
204
|
-
);
|
|
205
|
-
|
|
206
|
-
return {
|
|
207
|
-
contentStyle: {
|
|
208
|
-
transform: [{ translateX }],
|
|
209
|
-
},
|
|
210
|
-
};
|
|
211
|
-
},
|
|
212
|
-
transitionSpec: {
|
|
213
|
-
open: Transition.Specs.DefaultSpec,
|
|
214
|
-
close: Transition.Specs.DefaultSpec,
|
|
215
|
-
},
|
|
216
|
-
}}
|
|
217
|
-
/>;
|
|
111
|
+
```
|
|
112
|
+
0 ─────────── 1 ─────────── 2
|
|
113
|
+
entering visible exiting
|
|
218
114
|
```
|
|
219
115
|
|
|
220
|
-
|
|
116
|
+
When navigating from A to B:
|
|
117
|
+
- **Screen B**: progress goes `0 → 1` (entering)
|
|
118
|
+
- **Screen A**: progress goes `1 → 2` (exiting)
|
|
221
119
|
|
|
222
|
-
|
|
223
|
-
| ---------------- | -------------------------------------------------------- |
|
|
224
|
-
| `progress` | Combined progress (0-2). 0=entering, 1=active, 2=exiting |
|
|
225
|
-
| `stackProgress` | Accumulated progress across entire stack (0, 1, 2, 3...) |
|
|
226
|
-
| `current` | Current screen state (progress, closing, gesture, meta) |
|
|
227
|
-
| `previous` | Previous screen state (may be undefined) |
|
|
228
|
-
| `next` | Next screen state (may be undefined) |
|
|
229
|
-
| `layouts.screen` | Screen dimensions `{ width, height }` |
|
|
230
|
-
| `insets` | Safe area insets `{ top, right, bottom, left }` |
|
|
231
|
-
| `focused` | Whether current screen is the topmost |
|
|
232
|
-
| `active` | The screen driving the transition |
|
|
233
|
-
| `inactive` | The screen NOT driving the transition |
|
|
234
|
-
| `bounds` | Function to access shared element positions |
|
|
120
|
+
### Simple Fade
|
|
235
121
|
|
|
236
|
-
|
|
122
|
+
```tsx
|
|
123
|
+
options={{
|
|
124
|
+
screenStyleInterpolator: ({ progress }) => {
|
|
125
|
+
"worklet";
|
|
126
|
+
return {
|
|
127
|
+
contentStyle: {
|
|
128
|
+
opacity: interpolate(progress, [0, 1, 2], [0, 1, 0]),
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
},
|
|
132
|
+
}}
|
|
133
|
+
```
|
|
237
134
|
|
|
238
|
-
|
|
135
|
+
### Slide from Right
|
|
239
136
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
137
|
+
```tsx
|
|
138
|
+
options={{
|
|
139
|
+
screenStyleInterpolator: ({ progress, layouts: { screen } }) => {
|
|
140
|
+
"worklet";
|
|
141
|
+
return {
|
|
142
|
+
contentStyle: {
|
|
143
|
+
transform: [{
|
|
144
|
+
translateX: interpolate(
|
|
145
|
+
progress,
|
|
146
|
+
[0, 1, 2],
|
|
147
|
+
[screen.width, 0, -screen.width * 0.3]
|
|
148
|
+
),
|
|
149
|
+
}],
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
},
|
|
153
|
+
}}
|
|
154
|
+
```
|
|
247
155
|
|
|
248
|
-
###
|
|
156
|
+
### Slide from Bottom
|
|
249
157
|
|
|
250
|
-
|
|
158
|
+
```tsx
|
|
159
|
+
options={{
|
|
160
|
+
screenStyleInterpolator: ({ progress, layouts: { screen } }) => {
|
|
161
|
+
"worklet";
|
|
162
|
+
return {
|
|
163
|
+
contentStyle: {
|
|
164
|
+
transform: [{
|
|
165
|
+
translateY: interpolate(progress, [0, 1], [screen.height, 0]),
|
|
166
|
+
}],
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
},
|
|
170
|
+
}}
|
|
171
|
+
```
|
|
251
172
|
|
|
252
|
-
|
|
253
|
-
- **`inactive`** – The screen NOT driving the transition. When focused, this is `previous`. When not focused, this is `current`.
|
|
173
|
+
### Return Styles
|
|
254
174
|
|
|
255
|
-
|
|
256
|
-
// Check if the inactive screen wants to disable an animation
|
|
257
|
-
const disableTranslateY = props.inactive?.meta?.disableTranslateYAnimation;
|
|
175
|
+
Your interpolator can return:
|
|
258
176
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
177
|
+
```tsx
|
|
178
|
+
return {
|
|
179
|
+
contentStyle: { ... }, // Main screen
|
|
180
|
+
overlayStyle: { ... }, // Semi-transparent backdrop
|
|
181
|
+
["my-id"]: { ... }, // Specific element via styleId
|
|
182
|
+
};
|
|
262
183
|
```
|
|
263
184
|
|
|
264
|
-
###
|
|
185
|
+
### Animation Specs
|
|
265
186
|
|
|
266
|
-
|
|
187
|
+
Control timing with spring configs:
|
|
267
188
|
|
|
268
189
|
```tsx
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
275
|
-
|
|
190
|
+
options={{
|
|
191
|
+
screenStyleInterpolator: myInterpolator,
|
|
192
|
+
transitionSpec: {
|
|
193
|
+
open: { stiffness: 1000, damping: 500, mass: 3 },
|
|
194
|
+
close: { stiffness: 1000, damping: 500, mass: 3 },
|
|
195
|
+
},
|
|
196
|
+
}}
|
|
197
|
+
```
|
|
276
198
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
contentStyle: {
|
|
290
|
-
transform: [{ translateY: disableY ? 0 : translateY }],
|
|
291
|
-
},
|
|
292
|
-
};
|
|
293
|
-
},
|
|
294
|
-
}}
|
|
295
|
-
/>
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Gestures
|
|
202
|
+
|
|
203
|
+
Enable swipe-to-dismiss:
|
|
204
|
+
|
|
205
|
+
```tsx
|
|
206
|
+
options={{
|
|
207
|
+
gestureEnabled: true,
|
|
208
|
+
gestureDirection: "vertical",
|
|
209
|
+
...Transition.Presets.SlideFromBottom(),
|
|
210
|
+
}}
|
|
296
211
|
```
|
|
297
212
|
|
|
298
|
-
|
|
213
|
+
### Gesture Options
|
|
214
|
+
|
|
215
|
+
| Option | Description |
|
|
216
|
+
| ------------------------- | -------------------------------------- |
|
|
217
|
+
| `gestureEnabled` | Enable swipe-to-dismiss |
|
|
218
|
+
| `gestureDirection` | Direction(s) for swipe gesture |
|
|
219
|
+
| `gestureActivationArea` | Where gesture can start |
|
|
220
|
+
| `gestureResponseDistance` | Pixel threshold for activation |
|
|
221
|
+
| `gestureVelocityImpact` | How much velocity affects dismissal |
|
|
222
|
+
|
|
223
|
+
### Gesture Direction
|
|
299
224
|
|
|
300
225
|
```tsx
|
|
301
|
-
|
|
226
|
+
gestureDirection: "horizontal" // swipe left to dismiss
|
|
227
|
+
gestureDirection: "horizontal-inverted" // swipe right to dismiss
|
|
228
|
+
gestureDirection: "vertical" // swipe down to dismiss
|
|
229
|
+
gestureDirection: "vertical-inverted" // swipe up to dismiss
|
|
230
|
+
gestureDirection: "bidirectional" // any direction
|
|
302
231
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
(props) => {
|
|
306
|
-
// React to next screen's meta
|
|
307
|
-
if (props.next?.meta?.scalesOthers) {
|
|
308
|
-
scale.value = withTiming(0);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
);
|
|
232
|
+
// Or combine multiple:
|
|
233
|
+
gestureDirection: ["horizontal", "vertical"]
|
|
312
234
|
```
|
|
313
235
|
|
|
314
|
-
###
|
|
236
|
+
### Gesture Activation Area
|
|
315
237
|
|
|
316
238
|
```tsx
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
239
|
+
// Simple - same for all edges
|
|
240
|
+
gestureActivationArea: "edge" // only from screen edges
|
|
241
|
+
gestureActivationArea: "screen" // anywhere on screen
|
|
242
|
+
|
|
243
|
+
// Per-side configuration
|
|
244
|
+
gestureActivationArea: {
|
|
245
|
+
left: "edge",
|
|
246
|
+
right: "screen",
|
|
247
|
+
top: "edge",
|
|
248
|
+
bottom: "screen",
|
|
249
|
+
}
|
|
322
250
|
```
|
|
323
251
|
|
|
324
|
-
###
|
|
252
|
+
### With ScrollViews
|
|
325
253
|
|
|
326
|
-
|
|
254
|
+
Use transition-aware scrollables so gestures work correctly:
|
|
327
255
|
|
|
328
256
|
```tsx
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
return {
|
|
333
|
-
"hero-image": {
|
|
334
|
-
opacity: interpolate(progress, [0, 1], [0, 1]),
|
|
335
|
-
transform: [{ scale: interpolate(progress, [0, 1], [0.8, 1]) }],
|
|
336
|
-
},
|
|
337
|
-
};
|
|
338
|
-
};
|
|
257
|
+
<Transition.ScrollView>
|
|
258
|
+
{/* content */}
|
|
259
|
+
</Transition.ScrollView>
|
|
339
260
|
|
|
340
|
-
|
|
341
|
-
<Transition.View styleId="hero-image">
|
|
342
|
-
<Image source={...} />
|
|
343
|
-
</Transition.View>
|
|
261
|
+
<Transition.FlatList data={items} renderItem={...} />
|
|
344
262
|
```
|
|
345
263
|
|
|
264
|
+
Gesture rules with scrollables:
|
|
265
|
+
- **vertical** – only activates when scrolled to top
|
|
266
|
+
- **vertical-inverted** – only activates when scrolled to bottom
|
|
267
|
+
- **horizontal** – only activates at left/right scroll edges
|
|
268
|
+
|
|
346
269
|
---
|
|
347
270
|
|
|
348
271
|
## Shared Elements (Bounds API)
|
|
349
272
|
|
|
350
|
-
Animate elements between screens by
|
|
273
|
+
Animate elements between screens by tagging them.
|
|
351
274
|
|
|
352
|
-
### 1. Tag
|
|
275
|
+
### 1. Tag the Source
|
|
353
276
|
|
|
354
277
|
```tsx
|
|
355
|
-
// Source screen
|
|
356
278
|
<Transition.Pressable
|
|
357
279
|
sharedBoundTag="avatar"
|
|
358
280
|
onPress={() => navigation.navigate("Profile")}
|
|
359
281
|
>
|
|
360
282
|
<Image source={avatar} style={{ width: 50, height: 50 }} />
|
|
361
283
|
</Transition.Pressable>
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### 2. Tag the Destination
|
|
362
287
|
|
|
363
|
-
|
|
288
|
+
```tsx
|
|
364
289
|
<Transition.View sharedBoundTag="avatar">
|
|
365
290
|
<Image source={avatar} style={{ width: 200, height: 200 }} />
|
|
366
291
|
</Transition.View>
|
|
367
292
|
```
|
|
368
293
|
|
|
369
|
-
###
|
|
294
|
+
### 3. Use in Interpolator
|
|
370
295
|
|
|
371
296
|
```tsx
|
|
372
297
|
screenStyleInterpolator: ({ bounds }) => {
|
|
373
298
|
"worklet";
|
|
374
|
-
|
|
375
|
-
const avatarStyles = bounds({
|
|
376
|
-
id: "avatar",
|
|
377
|
-
method: "transform", // "transform" | "size" | "content"
|
|
378
|
-
space: "relative", // "relative" | "absolute"
|
|
379
|
-
scaleMode: "match", // "match" | "none" | "uniform"
|
|
380
|
-
anchor: "center", // positioning anchor
|
|
381
|
-
});
|
|
382
|
-
|
|
383
299
|
return {
|
|
384
|
-
avatar:
|
|
300
|
+
avatar: bounds({ id: "avatar", method: "transform" }),
|
|
385
301
|
};
|
|
386
302
|
};
|
|
387
303
|
```
|
|
388
304
|
|
|
389
305
|
### Bounds Options
|
|
390
306
|
|
|
391
|
-
| Option | Values
|
|
392
|
-
| ----------- |
|
|
393
|
-
| `id` | string
|
|
394
|
-
| `method` | `"transform"` `"size"` `"content"`
|
|
395
|
-
| `space` | `"relative"` `"absolute"`
|
|
396
|
-
| `scaleMode` | `"match"` `"none"` `"uniform"`
|
|
397
|
-
| `
|
|
398
|
-
| `target` | `"bound"` `"fullscreen"` or custom | Destination target |
|
|
399
|
-
| `raw` | boolean | Return raw values instead of styles |
|
|
400
|
-
|
|
401
|
-
### Raw Values
|
|
307
|
+
| Option | Values | Description |
|
|
308
|
+
| ----------- | ---------------------------------- | ----------------------------- |
|
|
309
|
+
| `id` | string | The `sharedBoundTag` to match |
|
|
310
|
+
| `method` | `"transform"` `"size"` `"content"` | How to animate |
|
|
311
|
+
| `space` | `"relative"` `"absolute"` | Coordinate space |
|
|
312
|
+
| `scaleMode` | `"match"` `"none"` `"uniform"` | Aspect ratio handling |
|
|
313
|
+
| `raw` | boolean | Return raw values |
|
|
402
314
|
|
|
403
|
-
|
|
404
|
-
const raw = bounds({ id: "avatar", method: "transform", raw: true });
|
|
405
|
-
// { translateX, translateY, scaleX, scaleY }
|
|
406
|
-
```
|
|
315
|
+
---
|
|
407
316
|
|
|
408
|
-
|
|
317
|
+
## Overlays
|
|
409
318
|
|
|
410
|
-
|
|
319
|
+
Persistent UI that animates with the stack:
|
|
411
320
|
|
|
412
321
|
```tsx
|
|
413
|
-
|
|
414
|
-
|
|
322
|
+
const TabBar = ({ focusedIndex, progress }) => {
|
|
323
|
+
const style = useAnimatedStyle(() => ({
|
|
324
|
+
transform: [{ translateY: interpolate(progress.value, [0, 1], [100, 0]) }],
|
|
325
|
+
}));
|
|
326
|
+
return <Animated.View style={[styles.tabBar, style]} />;
|
|
327
|
+
};
|
|
415
328
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
329
|
+
<Stack.Screen
|
|
330
|
+
name="Home"
|
|
331
|
+
options={{
|
|
332
|
+
overlay: TabBar,
|
|
333
|
+
overlayShown: true,
|
|
334
|
+
}}
|
|
335
|
+
/>
|
|
336
|
+
```
|
|
419
337
|
|
|
420
|
-
|
|
421
|
-
const borderRadius = bounds.interpolateStyle("avatar", "borderRadius");
|
|
338
|
+
### Overlay Props
|
|
422
339
|
|
|
423
|
-
|
|
424
|
-
|
|
340
|
+
| Prop | Description |
|
|
341
|
+
| -------------- | ------------------------------ |
|
|
342
|
+
| `focusedRoute` | Currently focused route |
|
|
343
|
+
| `focusedIndex` | Index of focused screen |
|
|
344
|
+
| `routes` | All routes in the stack |
|
|
345
|
+
| `progress` | Stack progress (derived value) |
|
|
346
|
+
| `navigation` | Navigation prop |
|
|
347
|
+
| `meta` | Custom metadata from options |
|
|
425
348
|
|
|
426
|
-
|
|
427
|
-
avatar: {
|
|
428
|
-
...bounds({ id: "avatar" }),
|
|
429
|
-
borderRadius,
|
|
430
|
-
},
|
|
431
|
-
};
|
|
432
|
-
};
|
|
433
|
-
```
|
|
349
|
+
---
|
|
434
350
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
|
438
|
-
|
|
|
439
|
-
| `
|
|
351
|
+
## Transition Components
|
|
352
|
+
|
|
353
|
+
| Component | Description |
|
|
354
|
+
| ----------------------- | -------------------------------------- |
|
|
355
|
+
| `Transition.View` | Animated view with `sharedBoundTag` |
|
|
356
|
+
| `Transition.Pressable` | Pressable that measures bounds |
|
|
357
|
+
| `Transition.ScrollView` | ScrollView with gesture coordination |
|
|
358
|
+
| `Transition.FlatList` | FlatList with gesture coordination |
|
|
359
|
+
| `Transition.MaskedView` | For reveal effects (requires native) |
|
|
440
360
|
|
|
441
361
|
---
|
|
442
362
|
|
|
443
|
-
##
|
|
363
|
+
## Hooks
|
|
444
364
|
|
|
445
|
-
|
|
365
|
+
### useScreenAnimation
|
|
366
|
+
|
|
367
|
+
Access animation state inside a screen:
|
|
446
368
|
|
|
447
369
|
```tsx
|
|
448
|
-
|
|
449
|
-
name="Detail"
|
|
450
|
-
options={{
|
|
451
|
-
gestureEnabled: true,
|
|
452
|
-
gestureDirection: "vertical", // or "horizontal", ["vertical", "horizontal"]
|
|
453
|
-
gestureActivationArea: "edge", // or "screen", or { left: "edge", top: "screen" }
|
|
454
|
-
gestureResponseDistance: 50,
|
|
455
|
-
gestureVelocityImpact: 0.3,
|
|
456
|
-
}}
|
|
457
|
-
/>
|
|
458
|
-
```
|
|
370
|
+
import { useScreenAnimation } from "react-native-screen-transitions";
|
|
459
371
|
|
|
460
|
-
|
|
372
|
+
function DetailScreen() {
|
|
373
|
+
const animation = useScreenAnimation();
|
|
374
|
+
|
|
375
|
+
const style = useAnimatedStyle(() => ({
|
|
376
|
+
opacity: animation.value.current.progress,
|
|
377
|
+
}));
|
|
461
378
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
| `gestureDirection` | `"horizontal"` `"vertical"` `"horizontal-inverted"` `"vertical-inverted"` or array |
|
|
466
|
-
| `gestureActivationArea` | `"edge"` `"screen"` or per-side config |
|
|
467
|
-
| `gestureResponseDistance` | Distance threshold for gesture recognition |
|
|
468
|
-
| `gestureVelocityImpact` | How much velocity affects dismissal decision |
|
|
469
|
-
| `gestureDrivesProgress` | Whether gesture directly drives animation (default: true) |
|
|
379
|
+
return <Animated.View style={style}>...</Animated.View>;
|
|
380
|
+
}
|
|
381
|
+
```
|
|
470
382
|
|
|
471
|
-
###
|
|
383
|
+
### useScreenState
|
|
472
384
|
|
|
473
|
-
|
|
385
|
+
Get navigation state without animation values:
|
|
474
386
|
|
|
475
387
|
```tsx
|
|
476
|
-
import
|
|
388
|
+
import { useScreenState } from "react-native-screen-transitions";
|
|
477
389
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
390
|
+
function DetailScreen() {
|
|
391
|
+
const { index, focusedRoute, routes, navigation } = useScreenState();
|
|
392
|
+
// ...
|
|
393
|
+
}
|
|
394
|
+
```
|
|
482
395
|
|
|
483
|
-
|
|
484
|
-
data={items}
|
|
485
|
-
renderItem={...}
|
|
486
|
-
/>
|
|
396
|
+
### useHistory
|
|
487
397
|
|
|
488
|
-
|
|
489
|
-
const TransitionFlashList = Transition.createTransitionAwareComponent(
|
|
490
|
-
FlashList,
|
|
491
|
-
{ isScrollable: true }
|
|
492
|
-
);
|
|
493
|
-
```
|
|
398
|
+
Access navigation history across the app:
|
|
494
399
|
|
|
495
|
-
|
|
400
|
+
```tsx
|
|
401
|
+
import { useHistory } from "react-native-screen-transitions";
|
|
496
402
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
403
|
+
function MyComponent() {
|
|
404
|
+
const { getRecent, getPath } = useHistory();
|
|
405
|
+
|
|
406
|
+
const recentScreens = getRecent(5); // Last 5 screens
|
|
407
|
+
const path = getPath(fromKey, toKey); // Path between screens
|
|
408
|
+
}
|
|
409
|
+
```
|
|
500
410
|
|
|
501
411
|
---
|
|
502
412
|
|
|
503
|
-
##
|
|
413
|
+
## Advanced Animation Props
|
|
414
|
+
|
|
415
|
+
The full `screenStyleInterpolator` receives these props:
|
|
504
416
|
|
|
505
|
-
|
|
417
|
+
| Prop | Description |
|
|
418
|
+
| ---------------- | -------------------------------------------------------- |
|
|
419
|
+
| `progress` | Combined progress (0-2) |
|
|
420
|
+
| `stackProgress` | Accumulated progress across entire stack |
|
|
421
|
+
| `current` | Current screen state |
|
|
422
|
+
| `previous` | Previous screen state |
|
|
423
|
+
| `next` | Next screen state |
|
|
424
|
+
| `active` | Screen driving the transition |
|
|
425
|
+
| `inactive` | Screen NOT driving the transition |
|
|
426
|
+
| `layouts.screen` | Screen dimensions |
|
|
427
|
+
| `insets` | Safe area insets |
|
|
428
|
+
| `bounds` | Shared element bounds function |
|
|
429
|
+
|
|
430
|
+
### Screen State Properties
|
|
431
|
+
|
|
432
|
+
Each screen state (`current`, `previous`, `next`, `active`, `inactive`) contains:
|
|
433
|
+
|
|
434
|
+
| Property | Description |
|
|
435
|
+
| ----------- | ---------------------------------------- |
|
|
436
|
+
| `progress` | Animation progress (0 or 1) |
|
|
437
|
+
| `closing` | Whether closing (0 or 1) |
|
|
438
|
+
| `entering` | Whether entering (0 or 1) |
|
|
439
|
+
| `animating` | Whether animating (0 or 1) |
|
|
440
|
+
| `gesture` | Gesture values (x, y, normalized values) |
|
|
441
|
+
| `meta` | Custom metadata from options |
|
|
506
442
|
|
|
507
|
-
###
|
|
443
|
+
### Using `meta` for Conditional Logic
|
|
508
444
|
|
|
509
|
-
|
|
445
|
+
Pass custom data between screens:
|
|
510
446
|
|
|
511
447
|
```tsx
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
opacity: interpolate(overlayAnimation.value.progress, [0, 1], [0, 1]),
|
|
515
|
-
}));
|
|
448
|
+
// Screen A
|
|
449
|
+
options={{ meta: { hideTabBar: true } }}
|
|
516
450
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
</Animated.View>
|
|
523
|
-
);
|
|
451
|
+
// Screen B reads it
|
|
452
|
+
screenStyleInterpolator: (props) => {
|
|
453
|
+
"worklet";
|
|
454
|
+
const hideTabBar = props.inactive?.meta?.hideTabBar;
|
|
455
|
+
// ...
|
|
524
456
|
};
|
|
525
|
-
|
|
526
|
-
<Stack.Screen
|
|
527
|
-
name="Home"
|
|
528
|
-
options={{
|
|
529
|
-
overlay: FloatingHeader,
|
|
530
|
-
overlayMode: "float",
|
|
531
|
-
overlayShown: true,
|
|
532
|
-
}}
|
|
533
|
-
/>;
|
|
534
457
|
```
|
|
535
458
|
|
|
536
|
-
###
|
|
459
|
+
### Animate Individual Elements
|
|
537
460
|
|
|
538
|
-
|
|
461
|
+
Use `styleId` to target specific elements:
|
|
539
462
|
|
|
540
463
|
```tsx
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
464
|
+
// In options
|
|
465
|
+
screenStyleInterpolator: ({ progress }) => {
|
|
466
|
+
"worklet";
|
|
467
|
+
return {
|
|
468
|
+
"hero-image": {
|
|
469
|
+
opacity: interpolate(progress, [0, 1], [0, 1]),
|
|
470
|
+
},
|
|
471
|
+
};
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
// In component
|
|
475
|
+
<Transition.View styleId="hero-image">
|
|
476
|
+
<Image source={...} />
|
|
477
|
+
</Transition.View>
|
|
549
478
|
```
|
|
550
479
|
|
|
551
|
-
|
|
480
|
+
---
|
|
481
|
+
|
|
482
|
+
## Stack Types
|
|
483
|
+
|
|
484
|
+
All three stacks share the same animation API. Choose based on your needs:
|
|
485
|
+
|
|
486
|
+
| Stack | Best For |
|
|
487
|
+
| ------------------- | --------------------------------------------------------- |
|
|
488
|
+
| **Blank Stack** | Most apps. Full control, all features. |
|
|
489
|
+
| **Native Stack** | When you need native screen primitives. |
|
|
490
|
+
| **Component Stack** | Embedded flows, isolated from React Navigation. *(Experimental)* |
|
|
552
491
|
|
|
553
|
-
|
|
554
|
-
| ------------------ | --------------------------------------------------------- |
|
|
555
|
-
| `focusedRoute` | Currently focused route |
|
|
556
|
-
| `focusedIndex` | Index of focused screen |
|
|
557
|
-
| `routes` | All routes in the stack |
|
|
558
|
-
| `meta` | Custom metadata passed from screen options |
|
|
559
|
-
| `navigation` | Navigation prop |
|
|
560
|
-
| `overlayAnimation` | Animation values with `progress` accumulated across stack |
|
|
561
|
-
| `screenAnimation` | Animation values for the current focused screen |
|
|
492
|
+
### Blank Stack
|
|
562
493
|
|
|
563
|
-
|
|
494
|
+
The default choice. Pure JavaScript with native-level performance via `react-native-screens`.
|
|
564
495
|
|
|
565
496
|
```tsx
|
|
497
|
+
import { createBlankStackNavigator } from "react-native-screen-transitions/blank-stack";
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### Native Stack
|
|
501
|
+
|
|
502
|
+
Extends `@react-navigation/native-stack`. Requires `enableTransitions: true`.
|
|
503
|
+
|
|
504
|
+
```tsx
|
|
505
|
+
import { createNativeStackNavigator } from "react-native-screen-transitions/native-stack";
|
|
506
|
+
|
|
566
507
|
<Stack.Screen
|
|
508
|
+
name="Detail"
|
|
567
509
|
options={{
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
title: "Step 1",
|
|
571
|
-
showProgress: true,
|
|
572
|
-
},
|
|
510
|
+
enableTransitions: true,
|
|
511
|
+
...Transition.Presets.SlideFromBottom(),
|
|
573
512
|
}}
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
// In overlay
|
|
577
|
-
const MyOverlay = ({ meta }) => {
|
|
578
|
-
return <Text>{meta?.title}</Text>;
|
|
579
|
-
};
|
|
513
|
+
/>
|
|
580
514
|
```
|
|
581
515
|
|
|
582
|
-
|
|
516
|
+
### Component Stack (Experimental)
|
|
583
517
|
|
|
584
|
-
|
|
518
|
+
> **Note:** This API is experimental and may change based on community feedback.
|
|
585
519
|
|
|
586
|
-
|
|
587
|
-
| ----------------------- | ------------------------------------------------------ |
|
|
588
|
-
| `Transition.View` | Animated view, supports `styleId` and `sharedBoundTag` |
|
|
589
|
-
| `Transition.Pressable` | Pressable with bounds measurement on press |
|
|
590
|
-
| `Transition.ScrollView` | ScrollView with gesture coordination |
|
|
591
|
-
| `Transition.FlatList` | FlatList with gesture coordination |
|
|
592
|
-
| `Transition.MaskedView` | For clipping during shared element transitions |
|
|
593
|
-
|
|
594
|
-
### Creating Custom Components
|
|
520
|
+
Standalone navigator, not connected to React Navigation. Ideal for embedded flows.
|
|
595
521
|
|
|
596
522
|
```tsx
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
523
|
+
import { createComponentNavigator } from "react-native-screen-transitions/component-stack";
|
|
524
|
+
|
|
525
|
+
const Stack = createComponentNavigator();
|
|
526
|
+
|
|
527
|
+
<Stack.Navigator initialRoute="step1">
|
|
528
|
+
<Stack.Screen name="step1" component={Step1} />
|
|
529
|
+
<Stack.Screen name="step2" component={Step2} />
|
|
530
|
+
</Stack.Navigator>
|
|
601
531
|
```
|
|
602
532
|
|
|
603
533
|
---
|
|
604
534
|
|
|
605
|
-
##
|
|
535
|
+
## Caveats & Trade-offs
|
|
606
536
|
|
|
607
|
-
###
|
|
537
|
+
### Native Stack
|
|
608
538
|
|
|
609
|
-
|
|
539
|
+
The Native Stack uses transparent modal presentation to intercept transitions. This has trade-offs:
|
|
610
540
|
|
|
611
|
-
|
|
612
|
-
|
|
541
|
+
- **Delayed touch events** – Exiting screens may have briefly delayed touch response
|
|
542
|
+
- **beforeRemove listeners** – Relies on navigation lifecycle events
|
|
543
|
+
- **Rapid navigation** – Some edge cases with very fast navigation sequences
|
|
613
544
|
|
|
614
|
-
|
|
615
|
-
const animation = useScreenAnimation();
|
|
545
|
+
For most apps, Blank Stack avoids these issues entirely.
|
|
616
546
|
|
|
617
|
-
|
|
618
|
-
const { current } = animation.value;
|
|
619
|
-
return {
|
|
620
|
-
opacity: current.progress,
|
|
621
|
-
};
|
|
622
|
-
});
|
|
547
|
+
### Component Stack (Experimental)
|
|
623
548
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
549
|
+
- **No deep linking** – Routes aren't part of your URL structure
|
|
550
|
+
- **Isolated state** – Doesn't affect parent navigation
|
|
551
|
+
- **Touch pass-through** – Uses `pointerEvents="box-none"` by default
|
|
627
552
|
|
|
628
553
|
---
|
|
629
554
|
|
|
630
|
-
##
|
|
555
|
+
## Experimental Features
|
|
556
|
+
|
|
557
|
+
### High Refresh Rate
|
|
631
558
|
|
|
632
|
-
|
|
559
|
+
Force maximum refresh rate during transitions (for 90Hz/120Hz displays):
|
|
633
560
|
|
|
634
561
|
```tsx
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
damping: 500,
|
|
639
|
-
mass: 3,
|
|
640
|
-
overshootClamping: true,
|
|
641
|
-
},
|
|
642
|
-
close: {
|
|
643
|
-
stiffness: 1000,
|
|
644
|
-
damping: 500,
|
|
645
|
-
mass: 3,
|
|
646
|
-
overshootClamping: true,
|
|
647
|
-
},
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
// Or use the default
|
|
651
|
-
transitionSpec: {
|
|
652
|
-
open: Transition.Specs.DefaultSpec,
|
|
653
|
-
close: Transition.Specs.DefaultSpec,
|
|
654
|
-
}
|
|
562
|
+
options={{
|
|
563
|
+
experimental_enableHighRefreshRate: true,
|
|
564
|
+
}}
|
|
655
565
|
```
|
|
656
566
|
|
|
657
567
|
---
|
|
658
568
|
|
|
659
569
|
## Masked View Setup
|
|
660
570
|
|
|
661
|
-
Required for `SharedIGImage` and `SharedAppleMusic` presets. The masked view creates the "reveal" effect where content
|
|
571
|
+
Required for `SharedIGImage` and `SharedAppleMusic` presets. The masked view creates the "reveal" effect where content expands from the shared element.
|
|
662
572
|
|
|
663
573
|
> **Note**: Requires native code. Will not work in Expo Go.
|
|
664
574
|
|
|
@@ -673,11 +583,9 @@ npm install @react-native-masked-view/masked-view
|
|
|
673
583
|
cd ios && pod install
|
|
674
584
|
```
|
|
675
585
|
|
|
676
|
-
###
|
|
677
|
-
|
|
678
|
-
Here's a full example showing how to set up an Apple Music-style shared element transition:
|
|
586
|
+
### Full Example
|
|
679
587
|
|
|
680
|
-
**1. Source Screen** – Tag pressable elements
|
|
588
|
+
**1. Source Screen** – Tag pressable elements:
|
|
681
589
|
|
|
682
590
|
```tsx
|
|
683
591
|
// app/index.tsx
|
|
@@ -708,7 +616,7 @@ export default function HomeScreen() {
|
|
|
708
616
|
}
|
|
709
617
|
```
|
|
710
618
|
|
|
711
|
-
**2. Destination Screen** – Wrap
|
|
619
|
+
**2. Destination Screen** – Wrap with MaskedView and match the tag:
|
|
712
620
|
|
|
713
621
|
```tsx
|
|
714
622
|
// app/details.tsx
|
|
@@ -736,7 +644,7 @@ export default function DetailsScreen() {
|
|
|
736
644
|
}
|
|
737
645
|
```
|
|
738
646
|
|
|
739
|
-
**3. Layout** – Apply the
|
|
647
|
+
**3. Layout** – Apply the preset with dynamic tag:
|
|
740
648
|
|
|
741
649
|
```tsx
|
|
742
650
|
// app/_layout.tsx
|
|
@@ -762,198 +670,16 @@ export default function RootLayout() {
|
|
|
762
670
|
|
|
763
671
|
### How It Works
|
|
764
672
|
|
|
765
|
-
1. `Transition.Pressable` measures its bounds
|
|
766
|
-
2. `Transition.View` on the destination
|
|
767
|
-
3. `Transition.MaskedView` clips
|
|
768
|
-
4. The preset interpolates position, size, and
|
|
769
|
-
|
|
770
|
-
---
|
|
771
|
-
|
|
772
|
-
## Native Stack
|
|
773
|
-
|
|
774
|
-
For cases where you need native screen primitives, use the native stack integration. This extends `@react-navigation/native-stack` with custom transition support.
|
|
775
|
-
|
|
776
|
-
> **Note**: The native stack has limitations. It uses `beforeRemove` listeners and transparent modals to intercept transitions. The Blank Stack is recommended for most use cases.
|
|
777
|
-
|
|
778
|
-
### Setup
|
|
779
|
-
|
|
780
|
-
```tsx
|
|
781
|
-
import { createNativeStackNavigator } from "react-native-screen-transitions/native-stack";
|
|
782
|
-
|
|
783
|
-
const Stack = createNativeStackNavigator();
|
|
784
|
-
|
|
785
|
-
<Stack.Screen
|
|
786
|
-
name="Detail"
|
|
787
|
-
options={{
|
|
788
|
-
enableTransitions: true, // Required to enable custom transitions
|
|
789
|
-
...Transition.Presets.SlideFromBottom(),
|
|
790
|
-
}}
|
|
791
|
-
/>;
|
|
792
|
-
```
|
|
793
|
-
|
|
794
|
-
### Expo Router Setup
|
|
795
|
-
|
|
796
|
-
```tsx
|
|
797
|
-
import type {
|
|
798
|
-
ParamListBase,
|
|
799
|
-
StackNavigationState,
|
|
800
|
-
} from "@react-navigation/native";
|
|
801
|
-
import { withLayoutContext } from "expo-router";
|
|
802
|
-
import {
|
|
803
|
-
createNativeStackNavigator,
|
|
804
|
-
type NativeStackNavigationEventMap,
|
|
805
|
-
type NativeStackNavigationOptions,
|
|
806
|
-
} from "react-native-screen-transitions/native-stack";
|
|
807
|
-
|
|
808
|
-
const { Navigator } = createNativeStackNavigator();
|
|
809
|
-
|
|
810
|
-
export const Stack = withLayoutContext<
|
|
811
|
-
NativeStackNavigationOptions,
|
|
812
|
-
typeof Navigator,
|
|
813
|
-
StackNavigationState<ParamListBase>,
|
|
814
|
-
NativeStackNavigationEventMap
|
|
815
|
-
>(Navigator);
|
|
816
|
-
```
|
|
817
|
-
|
|
818
|
-
### Native Stack Options
|
|
819
|
-
|
|
820
|
-
All standard `@react-navigation/native-stack` options are available, plus:
|
|
821
|
-
|
|
822
|
-
| Option | Type | Description |
|
|
823
|
-
| ------------------------- | ---------------------------------------- | ------------------------------------------------------------------ |
|
|
824
|
-
| `enableTransitions` | `boolean` | Enable custom transitions (sets presentation to transparent modal) |
|
|
825
|
-
| `screenStyleInterpolator` | `ScreenStyleInterpolator` | Function that returns animated styles |
|
|
826
|
-
| `transitionSpec` | `TransitionSpec` | Animation config for open/close |
|
|
827
|
-
| `gestureEnabled` | `boolean` | Whether swipe-to-dismiss is allowed |
|
|
828
|
-
| `gestureDirection` | `GestureDirection \| GestureDirection[]` | Allowed swipe directions |
|
|
829
|
-
| `gestureVelocityImpact` | `number` | How much velocity affects dismissal |
|
|
830
|
-
| `gestureResponseDistance` | `number` | Distance threshold for gesture |
|
|
831
|
-
| `gestureDrivesProgress` | `boolean` | Whether gesture drives animation |
|
|
832
|
-
| `gestureActivationArea` | `GestureActivationArea` | Where gesture can start |
|
|
833
|
-
| `meta` | `Record<string, unknown>` | Custom metadata for conditional animation logic |
|
|
834
|
-
|
|
835
|
-
### Renamed Native Options
|
|
836
|
-
|
|
837
|
-
To avoid collisions with custom gesture options, some native options are renamed:
|
|
838
|
-
|
|
839
|
-
| React Navigation | Renamed to |
|
|
840
|
-
| ------------------------- | ------------------------------- |
|
|
841
|
-
| `gestureDirection` | `nativeGestureDirection` |
|
|
842
|
-
| `gestureEnabled` | `nativeGestureEnabled` |
|
|
843
|
-
| `gestureResponseDistance` | `nativeGestureResponseDistance` |
|
|
844
|
-
|
|
845
|
-
### Limitations
|
|
846
|
-
|
|
847
|
-
- Relies on `beforeRemove` listener to intercept navigation
|
|
848
|
-
- Uses transparent modal presentation
|
|
849
|
-
- Some edge cases with rapid navigation
|
|
850
|
-
|
|
851
|
-
---
|
|
852
|
-
|
|
853
|
-
## Component Stack
|
|
854
|
-
|
|
855
|
-
A standalone navigator for when you don't need React Navigation integration. Perfect for embedded flows like onboarding, wizards, or any self-contained navigation.
|
|
856
|
-
|
|
857
|
-
### Setup
|
|
858
|
-
|
|
859
|
-
```tsx
|
|
860
|
-
import {
|
|
861
|
-
createComponentNavigator,
|
|
862
|
-
useComponentNavigation,
|
|
863
|
-
} from "react-native-screen-transitions/component-stack";
|
|
864
|
-
import Transition from "react-native-screen-transitions";
|
|
865
|
-
|
|
866
|
-
const Stack = createComponentNavigator();
|
|
867
|
-
|
|
868
|
-
function OnboardingFlow() {
|
|
869
|
-
return (
|
|
870
|
-
<Stack.Navigator initialRoute="step1">
|
|
871
|
-
<Stack.Screen
|
|
872
|
-
name="step1"
|
|
873
|
-
component={Step1}
|
|
874
|
-
options={{
|
|
875
|
-
...Transition.Presets.SlideFromBottom(),
|
|
876
|
-
}}
|
|
877
|
-
/>
|
|
878
|
-
<Stack.Screen
|
|
879
|
-
name="step2"
|
|
880
|
-
component={Step2}
|
|
881
|
-
options={{
|
|
882
|
-
...Transition.Presets.SlideFromBottom(),
|
|
883
|
-
}}
|
|
884
|
-
/>
|
|
885
|
-
</Stack.Navigator>
|
|
886
|
-
);
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
function Step1() {
|
|
890
|
-
const navigation = useComponentNavigation();
|
|
891
|
-
|
|
892
|
-
return (
|
|
893
|
-
<View>
|
|
894
|
-
<Text>Step 1</Text>
|
|
895
|
-
<Button title="Next" onPress={() => navigation.push("step2")} />
|
|
896
|
-
</View>
|
|
897
|
-
);
|
|
898
|
-
}
|
|
899
|
-
```
|
|
900
|
-
|
|
901
|
-
### Navigation API
|
|
902
|
-
|
|
903
|
-
```tsx
|
|
904
|
-
const navigation = useComponentNavigation();
|
|
905
|
-
|
|
906
|
-
navigation.push("screenName", { param: "value" }); // Push a screen
|
|
907
|
-
navigation.pop(); // Go back
|
|
908
|
-
navigation.popTo("screenName"); // Pop to a specific screen
|
|
909
|
-
navigation.reset("screenName"); // Reset to a single screen
|
|
910
|
-
```
|
|
911
|
-
|
|
912
|
-
### When to Use Component Stack
|
|
913
|
-
|
|
914
|
-
- **Onboarding flows** – Self-contained multi-step experiences
|
|
915
|
-
- **Wizards/Forms** – Multi-step forms within a screen
|
|
916
|
-
- **Embedded navigation** – Navigation inside a modal or sheet
|
|
917
|
-
- **Non-URL navigation** – When you don't need deep linking
|
|
918
|
-
|
|
919
|
-
---
|
|
920
|
-
|
|
921
|
-
## Experimental Features
|
|
922
|
-
|
|
923
|
-
### High Refresh Rate
|
|
924
|
-
|
|
925
|
-
Force the display to run at maximum refresh rate during transitions. This can make animations smoother on high refresh rate displays (90Hz, 120Hz).
|
|
926
|
-
|
|
927
|
-
```tsx
|
|
928
|
-
<Stack.Screen
|
|
929
|
-
name="Detail"
|
|
930
|
-
options={{
|
|
931
|
-
experimental_enableHighRefreshRate: true,
|
|
932
|
-
...Transition.Presets.SlideFromBottom(),
|
|
933
|
-
}}
|
|
934
|
-
/>
|
|
935
|
-
```
|
|
936
|
-
|
|
937
|
-
> **Note**: This is experimental and may have performance implications. Test on target devices.
|
|
938
|
-
|
|
939
|
-
---
|
|
940
|
-
|
|
941
|
-
## Migrating from Earlier Versions
|
|
942
|
-
|
|
943
|
-
### Deprecated Props
|
|
944
|
-
|
|
945
|
-
The following props are deprecated and will be removed in a future version:
|
|
946
|
-
|
|
947
|
-
| Deprecated Prop | Use Instead |
|
|
948
|
-
| ----------------------- | ------------------ |
|
|
949
|
-
| `isActiveTransitioning` | `active.animating` |
|
|
950
|
-
| `isDismissing` | `active.closing` |
|
|
673
|
+
1. `Transition.Pressable` measures its bounds on press and stores them with the tag
|
|
674
|
+
2. `Transition.View` on the destination registers as the target for that tag
|
|
675
|
+
3. `Transition.MaskedView` clips content to the animating shared element bounds
|
|
676
|
+
4. The preset interpolates position, size, and mask for a seamless expand/collapse effect
|
|
951
677
|
|
|
952
678
|
---
|
|
953
679
|
|
|
954
680
|
## Support
|
|
955
681
|
|
|
956
|
-
This package is developed in my spare time.
|
|
682
|
+
This package is developed in my spare time.
|
|
957
683
|
|
|
958
684
|
If you'd like to fuel the next release, [buy me a coffee](https://buymeacoffee.com/trpfsu)
|
|
959
685
|
|