react-native-screen-transitions 3.4.0-alpha.5 → 3.4.0-alpha.7
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 +1058 -11
- package/lib/commonjs/blank-stack/navigators/create-blank-stack-navigator.js +27 -45
- package/lib/commonjs/blank-stack/navigators/create-blank-stack-navigator.js.map +1 -1
- package/lib/commonjs/component-stack/navigators/create-component-stack-navigator.js +1 -1
- package/lib/commonjs/shared/components/create-boundary-component/components/boundary-target.js +39 -0
- package/lib/commonjs/shared/components/create-boundary-component/components/boundary-target.js.map +1 -0
- package/lib/commonjs/shared/components/create-boundary-component/create-boundary-component.js +232 -0
- package/lib/commonjs/shared/components/create-boundary-component/create-boundary-component.js.map +1 -0
- package/lib/commonjs/shared/components/create-boundary-component/hooks/helpers/measurement-rules.js +2 -4
- package/lib/commonjs/shared/components/create-boundary-component/hooks/helpers/measurement-rules.js.map +1 -1
- package/lib/commonjs/shared/components/create-boundary-component/hooks/use-boundary-measure-and-store.js +4 -3
- package/lib/commonjs/shared/components/create-boundary-component/hooks/use-boundary-measure-and-store.js.map +1 -1
- package/lib/commonjs/shared/components/create-boundary-component/hooks/use-pending-destination-retry-measurement.js +2 -1
- package/lib/commonjs/shared/components/create-boundary-component/hooks/use-pending-destination-retry-measurement.js.map +1 -1
- package/lib/commonjs/shared/components/create-boundary-component/index.js +20 -214
- package/lib/commonjs/shared/components/create-boundary-component/index.js.map +1 -1
- package/lib/commonjs/shared/components/create-boundary-component/providers/boundary-owner.provider.js +63 -0
- package/lib/commonjs/shared/components/create-boundary-component/providers/boundary-owner.provider.js.map +1 -0
- package/lib/commonjs/shared/components/create-transition-aware-component.js +29 -7
- package/lib/commonjs/shared/components/create-transition-aware-component.js.map +1 -1
- package/lib/commonjs/shared/components/overlay/variations/float-overlay.js +1 -1
- package/lib/commonjs/shared/components/screen-container/hooks/use-backdrop-pointer-events.js.map +1 -1
- package/lib/commonjs/shared/components/screen-container/index.js +12 -8
- package/lib/commonjs/shared/components/screen-container/index.js.map +1 -1
- package/lib/commonjs/shared/components/screen-container/layers/backdrop.js +8 -10
- package/lib/commonjs/shared/components/screen-container/layers/backdrop.js.map +1 -1
- package/lib/commonjs/shared/components/screen-container/layers/content.js +7 -10
- package/lib/commonjs/shared/components/screen-container/layers/content.js.map +1 -1
- package/lib/commonjs/shared/components/screen-container/layers/maybe-masked-navigation-container.js +4 -4
- package/lib/commonjs/shared/components/screen-container/layers/maybe-masked-navigation-container.js.map +1 -1
- package/lib/commonjs/shared/components/screen-container/layers/surface-container.js +4 -4
- package/lib/commonjs/shared/components/screen-container/layers/surface-container.js.map +1 -1
- package/lib/commonjs/shared/constants.js +4 -1
- package/lib/commonjs/shared/constants.js.map +1 -1
- package/lib/commonjs/shared/providers/screen/animation/helpers/hydrate-transition-state.js +33 -1
- package/lib/commonjs/shared/providers/screen/animation/helpers/hydrate-transition-state.js.map +1 -1
- package/lib/commonjs/shared/providers/screen/animation/helpers/pipeline.js +2 -0
- package/lib/commonjs/shared/providers/screen/animation/helpers/pipeline.js.map +1 -1
- package/lib/commonjs/shared/providers/screen/animation/helpers/use-build-transition-state.js +1 -0
- package/lib/commonjs/shared/providers/screen/animation/helpers/use-build-transition-state.js.map +1 -1
- package/lib/commonjs/shared/providers/screen/screen-composer.js +1 -1
- package/lib/commonjs/shared/providers/screen/screen-composer.js.map +1 -1
- package/lib/commonjs/shared/providers/screen/styles/helpers/build-resolved-style-map.js +144 -0
- package/lib/commonjs/shared/providers/screen/styles/helpers/build-resolved-style-map.js.map +1 -0
- package/lib/commonjs/shared/providers/screen/styles/helpers/resolve-interpolated-style-output.js +28 -0
- package/lib/commonjs/shared/providers/screen/styles/helpers/resolve-interpolated-style-output.js.map +1 -0
- package/lib/commonjs/shared/providers/screen/styles/helpers/split-normalized-style-maps.js +39 -0
- package/lib/commonjs/shared/providers/screen/styles/helpers/split-normalized-style-maps.js.map +1 -0
- package/lib/commonjs/shared/providers/screen/styles/index.js +25 -0
- package/lib/commonjs/shared/providers/screen/styles/index.js.map +1 -0
- package/lib/commonjs/shared/providers/screen/{styles.provider.js → styles/styles.provider.js} +47 -39
- package/lib/commonjs/shared/providers/screen/styles/styles.provider.js.map +1 -0
- package/lib/commonjs/shared/utils/bounds/helpers/compute-bounds-styles.js +1 -1
- package/lib/commonjs/shared/utils/bounds/helpers/compute-bounds-styles.js.map +1 -1
- package/lib/commonjs/shared/utils/bounds/helpers/style-composers.js +21 -10
- package/lib/commonjs/shared/utils/bounds/helpers/style-composers.js.map +1 -1
- package/lib/commonjs/shared/utils/bounds/zoom/build.js +57 -31
- package/lib/commonjs/shared/utils/bounds/zoom/build.js.map +1 -1
- package/lib/module/blank-stack/navigators/create-blank-stack-navigator.js +27 -45
- package/lib/module/blank-stack/navigators/create-blank-stack-navigator.js.map +1 -1
- package/lib/module/component-stack/navigators/create-component-stack-navigator.js +1 -1
- package/lib/module/shared/components/create-boundary-component/components/boundary-target.js +34 -0
- package/lib/module/shared/components/create-boundary-component/components/boundary-target.js.map +1 -0
- package/lib/module/shared/components/create-boundary-component/create-boundary-component.js +227 -0
- package/lib/module/shared/components/create-boundary-component/create-boundary-component.js.map +1 -0
- package/lib/module/shared/components/create-boundary-component/hooks/helpers/measurement-rules.js +2 -4
- package/lib/module/shared/components/create-boundary-component/hooks/helpers/measurement-rules.js.map +1 -1
- package/lib/module/shared/components/create-boundary-component/hooks/use-boundary-measure-and-store.js +4 -3
- package/lib/module/shared/components/create-boundary-component/hooks/use-boundary-measure-and-store.js.map +1 -1
- package/lib/module/shared/components/create-boundary-component/hooks/use-pending-destination-retry-measurement.js +2 -1
- package/lib/module/shared/components/create-boundary-component/hooks/use-pending-destination-retry-measurement.js.map +1 -1
- package/lib/module/shared/components/create-boundary-component/index.js +16 -213
- package/lib/module/shared/components/create-boundary-component/index.js.map +1 -1
- package/lib/module/shared/components/create-boundary-component/providers/boundary-owner.provider.js +56 -0
- package/lib/module/shared/components/create-boundary-component/providers/boundary-owner.provider.js.map +1 -0
- package/lib/module/shared/components/create-transition-aware-component.js +30 -8
- package/lib/module/shared/components/create-transition-aware-component.js.map +1 -1
- package/lib/module/shared/components/overlay/variations/float-overlay.js +1 -1
- package/lib/module/shared/components/overlay/variations/float-overlay.js.map +1 -1
- package/lib/module/shared/components/screen-container/hooks/use-backdrop-pointer-events.js.map +1 -1
- package/lib/module/shared/components/screen-container/index.js +12 -8
- package/lib/module/shared/components/screen-container/index.js.map +1 -1
- package/lib/module/shared/components/screen-container/layers/backdrop.js +8 -10
- package/lib/module/shared/components/screen-container/layers/backdrop.js.map +1 -1
- package/lib/module/shared/components/screen-container/layers/content.js +7 -10
- package/lib/module/shared/components/screen-container/layers/content.js.map +1 -1
- package/lib/module/shared/components/screen-container/layers/maybe-masked-navigation-container.js +4 -4
- package/lib/module/shared/components/screen-container/layers/maybe-masked-navigation-container.js.map +1 -1
- package/lib/module/shared/components/screen-container/layers/surface-container.js +4 -4
- package/lib/module/shared/components/screen-container/layers/surface-container.js.map +1 -1
- package/lib/module/shared/constants.js +4 -1
- package/lib/module/shared/constants.js.map +1 -1
- package/lib/module/shared/providers/screen/animation/helpers/hydrate-transition-state.js +32 -1
- package/lib/module/shared/providers/screen/animation/helpers/hydrate-transition-state.js.map +1 -1
- package/lib/module/shared/providers/screen/animation/helpers/pipeline.js +2 -0
- package/lib/module/shared/providers/screen/animation/helpers/pipeline.js.map +1 -1
- package/lib/module/shared/providers/screen/animation/helpers/use-build-transition-state.js +1 -0
- package/lib/module/shared/providers/screen/animation/helpers/use-build-transition-state.js.map +1 -1
- package/lib/module/shared/providers/screen/screen-composer.js +1 -1
- package/lib/module/shared/providers/screen/screen-composer.js.map +1 -1
- package/lib/module/shared/providers/screen/styles/helpers/build-resolved-style-map.js +139 -0
- package/lib/module/shared/providers/screen/styles/helpers/build-resolved-style-map.js.map +1 -0
- package/lib/module/shared/providers/screen/styles/helpers/resolve-interpolated-style-output.js +23 -0
- package/lib/module/shared/providers/screen/styles/helpers/resolve-interpolated-style-output.js.map +1 -0
- package/lib/module/shared/providers/screen/styles/helpers/split-normalized-style-maps.js +34 -0
- package/lib/module/shared/providers/screen/styles/helpers/split-normalized-style-maps.js.map +1 -0
- package/lib/module/shared/providers/screen/styles/index.js +4 -0
- package/lib/module/shared/providers/screen/styles/index.js.map +1 -0
- package/lib/module/shared/providers/screen/{styles.provider.js → styles/styles.provider.js} +48 -40
- package/lib/module/shared/providers/screen/styles/styles.provider.js.map +1 -0
- package/lib/module/shared/utils/bounds/helpers/compute-bounds-styles.js +2 -2
- package/lib/module/shared/utils/bounds/helpers/compute-bounds-styles.js.map +1 -1
- package/lib/module/shared/utils/bounds/helpers/style-composers.js +21 -10
- package/lib/module/shared/utils/bounds/helpers/style-composers.js.map +1 -1
- package/lib/module/shared/utils/bounds/zoom/build.js +56 -29
- package/lib/module/shared/utils/bounds/zoom/build.js.map +1 -1
- package/lib/typescript/blank-stack/navigators/create-blank-stack-navigator.d.ts +9 -15
- package/lib/typescript/blank-stack/navigators/create-blank-stack-navigator.d.ts.map +1 -1
- package/lib/typescript/blank-stack/types.d.ts +6 -4
- package/lib/typescript/blank-stack/types.d.ts.map +1 -1
- package/lib/typescript/component-stack/navigators/create-component-stack-navigator.d.ts +1 -1
- package/lib/typescript/shared/components/create-boundary-component/components/boundary-target.d.ts +209 -0
- package/lib/typescript/shared/components/create-boundary-component/components/boundary-target.d.ts.map +1 -0
- package/lib/typescript/shared/components/create-boundary-component/create-boundary-component.d.ts +8 -0
- package/lib/typescript/shared/components/create-boundary-component/create-boundary-component.d.ts.map +1 -0
- package/lib/typescript/shared/components/create-boundary-component/hooks/helpers/measurement-rules.d.ts.map +1 -1
- package/lib/typescript/shared/components/create-boundary-component/hooks/use-boundary-measure-and-store.d.ts +2 -8
- package/lib/typescript/shared/components/create-boundary-component/hooks/use-boundary-measure-and-store.d.ts.map +1 -1
- package/lib/typescript/shared/components/create-boundary-component/hooks/use-pending-destination-retry-measurement.d.ts +0 -1
- package/lib/typescript/shared/components/create-boundary-component/hooks/use-pending-destination-retry-measurement.d.ts.map +1 -1
- package/lib/typescript/shared/components/create-boundary-component/index.d.ts +216 -12
- package/lib/typescript/shared/components/create-boundary-component/index.d.ts.map +1 -1
- package/lib/typescript/shared/components/create-boundary-component/providers/boundary-owner.provider.d.ts +35 -0
- package/lib/typescript/shared/components/create-boundary-component/providers/boundary-owner.provider.d.ts.map +1 -0
- package/lib/typescript/shared/components/create-transition-aware-component.d.ts.map +1 -1
- package/lib/typescript/shared/components/screen-container/hooks/use-backdrop-pointer-events.d.ts +1 -1
- package/lib/typescript/shared/components/screen-container/hooks/use-backdrop-pointer-events.d.ts.map +1 -1
- package/lib/typescript/shared/components/screen-container/index.d.ts.map +1 -1
- package/lib/typescript/shared/components/screen-container/layers/backdrop.d.ts +5 -1
- package/lib/typescript/shared/components/screen-container/layers/backdrop.d.ts.map +1 -1
- package/lib/typescript/shared/components/screen-container/layers/content.d.ts +3 -1
- package/lib/typescript/shared/components/screen-container/layers/content.d.ts.map +1 -1
- package/lib/typescript/shared/components/screen-container/layers/maybe-masked-navigation-container.d.ts.map +1 -1
- package/lib/typescript/shared/constants.d.ts +1 -0
- package/lib/typescript/shared/constants.d.ts.map +1 -1
- package/lib/typescript/shared/index.d.ts +209 -2
- package/lib/typescript/shared/index.d.ts.map +1 -1
- package/lib/typescript/shared/providers/screen/animation/helpers/hydrate-transition-state.d.ts +16 -0
- package/lib/typescript/shared/providers/screen/animation/helpers/hydrate-transition-state.d.ts.map +1 -1
- package/lib/typescript/shared/providers/screen/animation/helpers/pipeline.d.ts.map +1 -1
- package/lib/typescript/shared/providers/screen/animation/helpers/use-build-transition-state.d.ts +1 -0
- package/lib/typescript/shared/providers/screen/animation/helpers/use-build-transition-state.d.ts.map +1 -1
- package/lib/typescript/shared/providers/screen/styles/helpers/build-resolved-style-map.d.ts +11 -0
- package/lib/typescript/shared/providers/screen/styles/helpers/build-resolved-style-map.d.ts.map +1 -0
- package/lib/typescript/shared/providers/screen/styles/helpers/resolve-interpolated-style-output.d.ts +7 -0
- package/lib/typescript/shared/providers/screen/styles/helpers/resolve-interpolated-style-output.d.ts.map +1 -0
- package/lib/typescript/shared/providers/screen/styles/helpers/split-normalized-style-maps.d.ts +6 -0
- package/lib/typescript/shared/providers/screen/styles/helpers/split-normalized-style-maps.d.ts.map +1 -0
- package/lib/typescript/shared/providers/screen/styles/index.d.ts +2 -0
- package/lib/typescript/shared/providers/screen/styles/index.d.ts.map +1 -0
- package/lib/typescript/shared/providers/screen/{styles.provider.d.ts → styles/styles.provider.d.ts} +3 -5
- package/lib/typescript/shared/providers/screen/styles/styles.provider.d.ts.map +1 -0
- package/lib/typescript/shared/types/animation.types.d.ts +16 -9
- package/lib/typescript/shared/types/animation.types.d.ts.map +1 -1
- package/lib/typescript/shared/types/index.d.ts +1 -1
- package/lib/typescript/shared/types/index.d.ts.map +1 -1
- package/lib/typescript/shared/types/screen.types.d.ts +4 -5
- package/lib/typescript/shared/types/screen.types.d.ts.map +1 -1
- package/lib/typescript/shared/utils/bounds/helpers/compute-bounds-styles.d.ts +1 -11
- package/lib/typescript/shared/utils/bounds/helpers/compute-bounds-styles.d.ts.map +1 -1
- package/lib/typescript/shared/utils/bounds/helpers/style-composers.d.ts.map +1 -1
- package/lib/typescript/shared/utils/bounds/zoom/build.d.ts +1 -1
- package/lib/typescript/shared/utils/bounds/zoom/build.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/blank-stack/navigators/create-blank-stack-navigator.tsx +37 -80
- package/src/blank-stack/types.ts +7 -4
- package/src/component-stack/navigators/create-component-stack-navigator.tsx +1 -1
- package/src/shared/components/create-boundary-component/components/boundary-target.tsx +45 -0
- package/src/shared/components/create-boundary-component/create-boundary-component.tsx +282 -0
- package/src/shared/components/create-boundary-component/hooks/helpers/measurement-rules.ts +2 -7
- package/src/shared/components/create-boundary-component/hooks/use-boundary-measure-and-store.ts +6 -10
- package/src/shared/components/create-boundary-component/hooks/use-pending-destination-retry-measurement.ts +3 -2
- package/src/shared/components/create-boundary-component/index.tsx +16 -252
- package/src/shared/components/create-boundary-component/providers/boundary-owner.provider.tsx +109 -0
- package/src/shared/components/create-transition-aware-component.tsx +33 -5
- package/src/shared/components/overlay/variations/float-overlay.tsx +1 -1
- package/src/shared/components/screen-container/hooks/use-backdrop-pointer-events.ts +1 -2
- package/src/shared/components/screen-container/index.tsx +12 -6
- package/src/shared/components/screen-container/layers/backdrop.tsx +13 -7
- package/src/shared/components/screen-container/layers/content.tsx +47 -43
- package/src/shared/components/screen-container/layers/maybe-masked-navigation-container.tsx +6 -4
- package/src/shared/components/screen-container/layers/surface-container.tsx +4 -4
- package/src/shared/constants.ts +3 -0
- package/src/shared/index.ts +0 -1
- package/src/shared/providers/screen/animation/helpers/hydrate-transition-state.ts +49 -1
- package/src/shared/providers/screen/animation/helpers/pipeline.ts +2 -0
- package/src/shared/providers/screen/animation/helpers/use-build-transition-state.ts +2 -0
- package/src/shared/providers/screen/screen-composer.tsx +1 -1
- package/src/shared/providers/screen/styles/helpers/build-resolved-style-map.ts +185 -0
- package/src/shared/providers/screen/styles/helpers/resolve-interpolated-style-output.ts +31 -0
- package/src/shared/providers/screen/styles/helpers/split-normalized-style-maps.ts +44 -0
- package/src/shared/providers/screen/styles/index.tsx +5 -0
- package/src/shared/providers/screen/styles/styles.provider.tsx +173 -0
- package/src/shared/types/animation.types.ts +18 -10
- package/src/shared/types/index.ts +0 -1
- package/src/shared/types/screen.types.ts +5 -5
- package/src/shared/utils/bounds/helpers/compute-bounds-styles.ts +2 -1
- package/src/shared/utils/bounds/helpers/style-composers.ts +11 -0
- package/src/shared/utils/bounds/zoom/build.ts +94 -59
- package/lib/commonjs/shared/components/screen-container/deferred-visibility-host.js +0 -43
- package/lib/commonjs/shared/components/screen-container/deferred-visibility-host.js.map +0 -1
- package/lib/commonjs/shared/hooks/animation/use-associated-style.js +0 -234
- package/lib/commonjs/shared/hooks/animation/use-associated-style.js.map +0 -1
- package/lib/commonjs/shared/providers/screen/helpers/resolve-interpolated-style-output.js +0 -50
- package/lib/commonjs/shared/providers/screen/helpers/resolve-interpolated-style-output.js.map +0 -1
- package/lib/commonjs/shared/providers/screen/styles.provider.js.map +0 -1
- package/lib/module/shared/components/screen-container/deferred-visibility-host.js +0 -38
- package/lib/module/shared/components/screen-container/deferred-visibility-host.js.map +0 -1
- package/lib/module/shared/hooks/animation/use-associated-style.js +0 -229
- package/lib/module/shared/hooks/animation/use-associated-style.js.map +0 -1
- package/lib/module/shared/providers/screen/helpers/resolve-interpolated-style-output.js +0 -44
- package/lib/module/shared/providers/screen/helpers/resolve-interpolated-style-output.js.map +0 -1
- package/lib/module/shared/providers/screen/styles.provider.js.map +0 -1
- package/lib/typescript/shared/components/screen-container/deferred-visibility-host.d.ts +0 -13
- package/lib/typescript/shared/components/screen-container/deferred-visibility-host.d.ts.map +0 -1
- package/lib/typescript/shared/hooks/animation/use-associated-style.d.ts +0 -32
- package/lib/typescript/shared/hooks/animation/use-associated-style.d.ts.map +0 -1
- package/lib/typescript/shared/providers/screen/helpers/resolve-interpolated-style-output.d.ts +0 -14
- package/lib/typescript/shared/providers/screen/helpers/resolve-interpolated-style-output.d.ts.map +0 -1
- package/lib/typescript/shared/providers/screen/styles.provider.d.ts.map +0 -1
- package/src/shared/components/screen-container/deferred-visibility-host.tsx +0 -37
- package/src/shared/hooks/animation/use-associated-style.ts +0 -297
- package/src/shared/providers/screen/helpers/resolve-interpolated-style-output.ts +0 -61
- package/src/shared/providers/screen/styles.provider.tsx +0 -164
package/README.md
CHANGED
|
@@ -1,14 +1,76 @@
|
|
|
1
1
|
# react-native-screen-transitions
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Customizable screen transitions for React Native. Build gesture-driven, shared element, and fully custom animations with a simple API.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
| iOS | Android |
|
|
6
|
+
| --------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
7
|
+
| <video src="https://github.com/user-attachments/assets/c0d17b8f-7268-421c-9051-e242f8ddca76" width="300" height="600" controls></video> | <video src="https://github.com/user-attachments/assets/3f8d5fb1-96d2-4fe3-860d-62f6fb5a687e" width="300" controls></video> |
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Full Animation Control** – Define exactly how screens enter, exit, and respond to gestures
|
|
12
|
+
- **Bounds API + Navigation Zoom** – Build shared element and fullscreen zoom transitions with one bounds helper
|
|
13
|
+
- **Auto Snap Points** – Use `snapPoints: ["auto"]` and read measured content layout inside your interpolator
|
|
14
|
+
- **Gesture-Aware Scrollables** – Transition-aware `ScrollView` and `FlatList` coordinate with dismiss and snap gestures
|
|
15
|
+
- **Backdrop + Surface Slots** – Animate screen content, backdrops, surfaces, and per-element slots from one interpolator
|
|
16
|
+
- **Ready-Made Presets** – Instagram, Apple Music, X (Twitter) style transitions included
|
|
17
|
+
|
|
18
|
+
## What's New In 3.4
|
|
19
|
+
|
|
20
|
+
3.4 introduces a newer, more explicit path for shared transitions and snap-driven layouts.
|
|
21
|
+
Use the notes below as the source of truth when migrating examples or generating docs.
|
|
22
|
+
|
|
23
|
+
### Added / Expanded
|
|
24
|
+
|
|
25
|
+
- **Auto snap sizing** with `snapPoints: ["auto"]` and `current.layouts.content`
|
|
26
|
+
- **Compound bounds components** via `Transition.Boundary.View`, `Transition.Boundary.Trigger`, and `Transition.Boundary.Target`
|
|
27
|
+
- **`Transition.createBoundaryComponent`** for building custom boundary wrappers, including `alreadyAnimated` support
|
|
28
|
+
- **Navigation-style bounds zoom** through `bounds({ id }).navigation.zoom()`
|
|
29
|
+
- **`navigationMaskEnabled`** for library-managed masked navigation transitions
|
|
30
|
+
- **Ancestor targeting** in `useScreenGesture()` and `useScreenAnimation()`
|
|
31
|
+
- **Gesture release tuning** with `gestureReleaseVelocityScale` and `gestureReleaseVelocityMax`
|
|
32
|
+
- **Surface slot support** through `surfaceComponent` and the interpolator `surface` slot
|
|
33
|
+
- **Animated `props` support across all slots** via `{ style, props }` slot returns
|
|
34
|
+
- **Optional first-screen animation** with `experimental_animateOnInitialMount`
|
|
35
|
+
- **`logicallySettled`** for choreography that should finish before a spring is fully at rest
|
|
36
|
+
|
|
37
|
+
### Deprecated / Replaced
|
|
38
|
+
|
|
39
|
+
- **`sharedBoundTag` on transition-aware components is deprecated for new work.** Prefer `Transition.Boundary.*` for new shared transition flows.
|
|
40
|
+
- **`Transition.MaskedView` is deprecated for new work.** Prefer `Transition.Boundary.*` with `bounds({ id }).navigation.zoom()` and `navigationMaskEnabled` for library-managed shared navigation transitions.
|
|
41
|
+
- **`createComponentStackNavigator` is deprecated.** Prefer blank stack for embedded and independent flows.
|
|
42
|
+
- **`expandViaScrollView` was renamed to `sheetScrollGestureBehavior`.**
|
|
43
|
+
- **Flat interpolator keys are deprecated.** Use `content`, `backdrop`, and `surface` instead of `contentStyle`, `backdropStyle`, and `overlayStyle`.
|
|
44
|
+
- **Legacy interpolator accessors are deprecated.** Use `current.layouts` and `current.snapIndex` instead of top-level `layouts` and `snapIndex`.
|
|
45
|
+
- **If you saw older alpha docs using `backgroundComponent` / `background`, use `surfaceComponent` / `surface`.**
|
|
46
|
+
|
|
47
|
+
### Removed
|
|
48
|
+
|
|
49
|
+
- Deprecated screen overlay mode and legacy overlay animation props were removed.
|
|
50
|
+
|
|
51
|
+
## When to Use This Library
|
|
52
|
+
|
|
53
|
+
| Use Case | This Library | Alternative |
|
|
54
|
+
|----------|--------------|-------------|
|
|
55
|
+
| Custom transitions (slide, zoom, fade variations) | Yes | `@react-navigation/stack` works too |
|
|
56
|
+
| Shared element transitions | **Yes** | Limited options elsewhere |
|
|
57
|
+
| Multi-stop sheets (bottom, top, side) with snap points | **Yes** | Dedicated sheet libraries |
|
|
58
|
+
| Gesture-driven animations (drag to dismiss, elastic) | **Yes** | Requires custom implementation |
|
|
59
|
+
| Instagram/Apple Music/Twitter-style transitions | **Yes** | Custom implementation |
|
|
60
|
+
| Simple push/pop with platform defaults | Overkill | `@react-navigation/native-stack` |
|
|
61
|
+
| Maximum raw performance on low-end devices | Not ideal | `@react-navigation/native-stack` |
|
|
62
|
+
|
|
63
|
+
**Choose this library when** you need custom animations, shared elements, or gesture-driven transitions that go beyond platform defaults.
|
|
64
|
+
|
|
65
|
+
**Choose native-stack when** you want platform-native transitions with zero configuration and maximum performance on low-end Android devices.
|
|
66
|
+
|
|
67
|
+
## Installation
|
|
6
68
|
|
|
7
69
|
```bash
|
|
8
70
|
npm install react-native-screen-transitions
|
|
9
71
|
```
|
|
10
72
|
|
|
11
|
-
Peer
|
|
73
|
+
### Peer Dependencies
|
|
12
74
|
|
|
13
75
|
```bash
|
|
14
76
|
npm install react-native-reanimated react-native-gesture-handler \
|
|
@@ -17,15 +79,19 @@ npm install react-native-reanimated react-native-gesture-handler \
|
|
|
17
79
|
react-native-safe-area-context
|
|
18
80
|
```
|
|
19
81
|
|
|
20
|
-
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Quick Start
|
|
85
|
+
|
|
86
|
+
### 1. Create a Stack
|
|
21
87
|
|
|
22
88
|
```tsx
|
|
23
|
-
import Transition from "react-native-screen-transitions";
|
|
24
89
|
import { createBlankStackNavigator } from "react-native-screen-transitions/blank-stack";
|
|
90
|
+
import Transition from "react-native-screen-transitions";
|
|
25
91
|
|
|
26
92
|
const Stack = createBlankStackNavigator();
|
|
27
93
|
|
|
28
|
-
|
|
94
|
+
function App() {
|
|
29
95
|
return (
|
|
30
96
|
<Stack.Navigator>
|
|
31
97
|
<Stack.Screen name="Home" component={HomeScreen} />
|
|
@@ -41,9 +107,990 @@ export function AppStack() {
|
|
|
41
107
|
}
|
|
42
108
|
```
|
|
43
109
|
|
|
44
|
-
|
|
110
|
+
### 2. With Expo Router
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
import { withLayoutContext } from "expo-router";
|
|
114
|
+
import {
|
|
115
|
+
createBlankStackNavigator,
|
|
116
|
+
type BlankStackNavigationOptions,
|
|
117
|
+
} from "react-native-screen-transitions/blank-stack";
|
|
118
|
+
|
|
119
|
+
const { Navigator } = createBlankStackNavigator();
|
|
120
|
+
|
|
121
|
+
export const Stack = withLayoutContext<
|
|
122
|
+
BlankStackNavigationOptions,
|
|
123
|
+
typeof Navigator
|
|
124
|
+
>(Navigator);
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 3. Static Config
|
|
128
|
+
|
|
129
|
+
Blank stack-specific navigator props follow React Navigation's custom navigator pattern.
|
|
130
|
+
|
|
131
|
+
For the dynamic API, pass them to `<Stack.Navigator>`:
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
const Stack = createBlankStackNavigator();
|
|
135
|
+
|
|
136
|
+
function App() {
|
|
137
|
+
return (
|
|
138
|
+
<Stack.Navigator independent enableNativeScreens={false}>
|
|
139
|
+
<Stack.Screen name="Home" component={HomeScreen} />
|
|
140
|
+
<Stack.Screen name="Detail" component={DetailScreen} />
|
|
141
|
+
</Stack.Navigator>
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
For the static API, keep them in the same config object:
|
|
147
|
+
|
|
148
|
+
```tsx
|
|
149
|
+
import { createBlankStackNavigator } from "react-native-screen-transitions/blank-stack";
|
|
150
|
+
|
|
151
|
+
const Stack = createBlankStackNavigator({
|
|
152
|
+
initialRouteName: "Home",
|
|
153
|
+
screens: {
|
|
154
|
+
Home: HomeScreen,
|
|
155
|
+
Detail: DetailScreen,
|
|
156
|
+
},
|
|
157
|
+
independent: true,
|
|
158
|
+
enableNativeScreens: false,
|
|
159
|
+
});
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Presets
|
|
165
|
+
|
|
166
|
+
Use built-in presets for common transitions:
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
<Stack.Screen
|
|
170
|
+
name="Detail"
|
|
171
|
+
options={{
|
|
172
|
+
...Transition.Presets.SlideFromBottom(),
|
|
173
|
+
}}
|
|
174
|
+
/>
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
| Preset | Description |
|
|
178
|
+
| -------------------------------------- | --------------------------------------- |
|
|
179
|
+
| `SlideFromTop()` | Slides in from top |
|
|
180
|
+
| `SlideFromBottom()` | Slides in from bottom (modal-style) |
|
|
181
|
+
| `ZoomIn()` | Scales in with fade |
|
|
182
|
+
| `DraggableCard()` | Multi-directional drag with scaling |
|
|
183
|
+
| `ElasticCard()` | Elastic drag with overlay |
|
|
184
|
+
| `SharedIGImage({ sharedBoundTag })` | Legacy Instagram-style shared image preset |
|
|
185
|
+
| `SharedAppleMusic({ sharedBoundTag })` | Legacy Apple Music-style shared element preset |
|
|
186
|
+
| `SharedXImage({ sharedBoundTag })` | Legacy X (Twitter)-style image transition preset |
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Custom Animations
|
|
191
|
+
|
|
192
|
+
### The Basics
|
|
193
|
+
|
|
194
|
+
Every screen has a `progress` value that goes from 0 → 1 → 2:
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
0 ─────────── 1 ─────────── 2
|
|
198
|
+
entering visible exiting
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
When navigating from A to B:
|
|
202
|
+
- **Screen B**: progress goes `0 → 1` (entering)
|
|
203
|
+
- **Screen A**: progress goes `1 → 2` (exiting)
|
|
204
|
+
|
|
205
|
+
### Simple Fade
|
|
206
|
+
|
|
207
|
+
```tsx
|
|
208
|
+
options={{
|
|
209
|
+
screenStyleInterpolator: ({ progress }) => {
|
|
210
|
+
"worklet";
|
|
211
|
+
return {
|
|
212
|
+
content: {
|
|
213
|
+
style: {
|
|
214
|
+
opacity: interpolate(progress, [0, 1, 2], [0, 1, 0]),
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
},
|
|
219
|
+
}}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Slide from Right
|
|
223
|
+
|
|
224
|
+
```tsx
|
|
225
|
+
options={{
|
|
226
|
+
screenStyleInterpolator: ({ progress, current: { layouts: { screen } } }) => {
|
|
227
|
+
"worklet";
|
|
228
|
+
return {
|
|
229
|
+
content: {
|
|
230
|
+
style: {
|
|
231
|
+
transform: [{
|
|
232
|
+
translateX: interpolate(
|
|
233
|
+
progress,
|
|
234
|
+
[0, 1, 2],
|
|
235
|
+
[screen.width, 0, -screen.width * 0.3]
|
|
236
|
+
),
|
|
237
|
+
}],
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
},
|
|
242
|
+
}}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Slide from Bottom
|
|
246
|
+
|
|
247
|
+
```tsx
|
|
248
|
+
options={{
|
|
249
|
+
screenStyleInterpolator: ({ progress, current: { layouts: { screen } } }) => {
|
|
250
|
+
"worklet";
|
|
251
|
+
return {
|
|
252
|
+
content: {
|
|
253
|
+
style: {
|
|
254
|
+
transform: [{
|
|
255
|
+
translateY: interpolate(progress, [0, 1], [screen.height, 0]),
|
|
256
|
+
}],
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
},
|
|
261
|
+
}}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Return Styles
|
|
265
|
+
|
|
266
|
+
Your interpolator can return:
|
|
267
|
+
|
|
268
|
+
```tsx
|
|
269
|
+
return {
|
|
270
|
+
content: { style: { ... }, props: { ... } }, // Main screen slot
|
|
271
|
+
backdrop: { style: { ... }, props: { ... } }, // Backdrop / blur / dimming
|
|
272
|
+
surface: { style: { ... }, props: { ... } }, // Custom surface layer
|
|
273
|
+
["my-id"]: { style: { ... }, props: { ... } }, // Specific element via styleId
|
|
274
|
+
};
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Every slot supports animated `style` and animated `props`.
|
|
278
|
+
Use the shorthand style-only form when you only need styles, or the explicit
|
|
279
|
+
`{ style, props }` form when the wrapped component exposes animatable props.
|
|
280
|
+
|
|
281
|
+
Return `null`, `undefined`, or `{}` when you want an interpolator frame to apply no transition styles:
|
|
282
|
+
|
|
283
|
+
```tsx
|
|
284
|
+
screenStyleInterpolator: ({ bounds }) => {
|
|
285
|
+
"worklet";
|
|
286
|
+
|
|
287
|
+
const snapshot = bounds.getSnapshot("hero");
|
|
288
|
+
if (!snapshot) return null;
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
content: {
|
|
292
|
+
style: { opacity: 1 },
|
|
293
|
+
},
|
|
294
|
+
};
|
|
295
|
+
};
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Animation Specs
|
|
299
|
+
|
|
300
|
+
Control timing with spring configs:
|
|
301
|
+
|
|
302
|
+
```tsx
|
|
303
|
+
options={{
|
|
304
|
+
screenStyleInterpolator: myInterpolator,
|
|
305
|
+
transitionSpec: {
|
|
306
|
+
open: { stiffness: 1000, damping: 500, mass: 3 }, // Screen enters
|
|
307
|
+
close: { stiffness: 1000, damping: 500, mass: 3 }, // Screen exits
|
|
308
|
+
expand: { stiffness: 300, damping: 30 }, // Snap point increases
|
|
309
|
+
collapse: { stiffness: 300, damping: 30 }, // Snap point decreases
|
|
310
|
+
},
|
|
311
|
+
}}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Gestures
|
|
317
|
+
|
|
318
|
+
Enable swipe-to-dismiss:
|
|
319
|
+
|
|
320
|
+
```tsx
|
|
321
|
+
options={{
|
|
322
|
+
gestureEnabled: true,
|
|
323
|
+
gestureDirection: "vertical",
|
|
324
|
+
...Transition.Presets.SlideFromBottom(),
|
|
325
|
+
}}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Gesture Options
|
|
329
|
+
|
|
330
|
+
| Option | Description |
|
|
331
|
+
| ------------------------- | ------------------------------------------------------------------------ |
|
|
332
|
+
| `gestureEnabled` | Enable swipe-to-dismiss (snap sheets: `false` blocks dismiss-to-0 only) |
|
|
333
|
+
| `gestureDirection` | Direction(s) for swipe gesture |
|
|
334
|
+
| `gestureActivationArea` | Where gesture can start |
|
|
335
|
+
| `gestureResponseDistance` | Pixel threshold for activation |
|
|
336
|
+
| `gestureVelocityImpact` | How much velocity affects dismissal (default: 0.3) |
|
|
337
|
+
| `gestureDrivesProgress` | Whether gesture controls animation progress (default: true) |
|
|
338
|
+
| `snapVelocityImpact` | How much velocity affects snap targeting (default: 0.1, lower = iOS-like)|
|
|
339
|
+
| `gestureReleaseVelocityScale` | Multiplier for release velocity used by post-release spring animations |
|
|
340
|
+
| `gestureReleaseVelocityMax` | Max absolute normalized release velocity used by spring animations |
|
|
341
|
+
| `sheetScrollGestureBehavior` | Nested scroll handoff mode: `"expand-and-collapse"` or `"collapse-only"` |
|
|
342
|
+
| `gestureSnapLocked` | Lock gesture-based snap movement to current snap point |
|
|
343
|
+
| `backdropBehavior` | Touch handling for backdrop area |
|
|
344
|
+
| `backdropComponent` | Custom backdrop component (replaces default backdrop + press behavior) |
|
|
345
|
+
|
|
346
|
+
### Gesture Direction
|
|
347
|
+
|
|
348
|
+
```tsx
|
|
349
|
+
gestureDirection: "horizontal" // swipe left to dismiss
|
|
350
|
+
gestureDirection: "horizontal-inverted" // swipe right to dismiss
|
|
351
|
+
gestureDirection: "vertical" // swipe down to dismiss
|
|
352
|
+
gestureDirection: "vertical-inverted" // swipe up to dismiss
|
|
353
|
+
gestureDirection: "bidirectional" // any direction
|
|
354
|
+
|
|
355
|
+
// Or combine multiple:
|
|
356
|
+
gestureDirection: ["horizontal", "vertical"]
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Gesture Activation Area
|
|
360
|
+
|
|
361
|
+
```tsx
|
|
362
|
+
// Simple - same for all edges
|
|
363
|
+
gestureActivationArea: "edge" // only from screen edges
|
|
364
|
+
gestureActivationArea: "screen" // anywhere on screen
|
|
365
|
+
|
|
366
|
+
// Per-side configuration
|
|
367
|
+
gestureActivationArea: {
|
|
368
|
+
left: "edge",
|
|
369
|
+
right: "screen",
|
|
370
|
+
top: "edge",
|
|
371
|
+
bottom: "screen",
|
|
372
|
+
}
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### With ScrollViews
|
|
376
|
+
|
|
377
|
+
Use transition-aware scrollables so gestures work correctly:
|
|
378
|
+
|
|
379
|
+
```tsx
|
|
380
|
+
<Transition.ScrollView>
|
|
381
|
+
{/* content */}
|
|
382
|
+
</Transition.ScrollView>
|
|
383
|
+
|
|
384
|
+
<Transition.FlatList data={items} renderItem={...} />
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
Gesture rules with scrollables:
|
|
388
|
+
- **vertical** – only activates when scrolled to top
|
|
389
|
+
- **vertical-inverted** – only activates when scrolled to bottom
|
|
390
|
+
- **horizontal** – only activates at left/right scroll edges
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
## Snap Points
|
|
395
|
+
|
|
396
|
+
Create multi-stop sheets that snap to defined positions. Works with any gesture direction (bottom sheets, top sheets, side sheets), and supports intrinsic content sizing with `"auto"` snap points.
|
|
397
|
+
|
|
398
|
+
### Basic Configuration
|
|
399
|
+
|
|
400
|
+
```tsx
|
|
401
|
+
// Bottom sheet (most common)
|
|
402
|
+
<Stack.Screen
|
|
403
|
+
name="Sheet"
|
|
404
|
+
options={{
|
|
405
|
+
gestureEnabled: true,
|
|
406
|
+
gestureDirection: "vertical",
|
|
407
|
+
snapPoints: [0.5, 1], // 50% and 100% of screen
|
|
408
|
+
initialSnapIndex: 0, // Start at 50%
|
|
409
|
+
backdropBehavior: "dismiss", // Tap backdrop to dismiss
|
|
410
|
+
...Transition.Presets.SlideFromBottom(),
|
|
411
|
+
}}
|
|
412
|
+
/>
|
|
413
|
+
|
|
414
|
+
// Side sheet (same API, different direction)
|
|
415
|
+
<Stack.Screen
|
|
416
|
+
name="SidePanel"
|
|
417
|
+
options={{
|
|
418
|
+
gestureEnabled: true,
|
|
419
|
+
gestureDirection: "horizontal",
|
|
420
|
+
snapPoints: [0.3, 0.7, 1], // 30%, 70%, 100% of screen width
|
|
421
|
+
initialSnapIndex: 1,
|
|
422
|
+
// Add a horizontal screenStyleInterpolator for drawer-style motion
|
|
423
|
+
}}
|
|
424
|
+
/>
|
|
425
|
+
|
|
426
|
+
// Auto-sized sheet
|
|
427
|
+
<Stack.Screen
|
|
428
|
+
name="Composer"
|
|
429
|
+
options={{
|
|
430
|
+
gestureEnabled: true,
|
|
431
|
+
gestureDirection: "vertical",
|
|
432
|
+
snapPoints: ["auto", 1],
|
|
433
|
+
initialSnapIndex: 0,
|
|
434
|
+
backdropBehavior: "collapse",
|
|
435
|
+
...Transition.Presets.SlideFromBottom(),
|
|
436
|
+
}}
|
|
437
|
+
/>
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### Options
|
|
441
|
+
|
|
442
|
+
| Option | Description |
|
|
443
|
+
| ------------------ | -------------------------------------------------------------------- |
|
|
444
|
+
| `snapPoints` | Array of fractions (0-1) or `"auto"` values where sheet can rest |
|
|
445
|
+
| `initialSnapIndex` | Index of initial snap point (default: 0) |
|
|
446
|
+
| `gestureSnapLocked` | Locks gesture snapping to current point (programmatic `snapTo` still works) |
|
|
447
|
+
| `sheetScrollGestureBehavior` | Nested scroll handoff mode for snap sheets |
|
|
448
|
+
| `backdropBehavior` | Touch handling: `"block"`, `"passthrough"`, `"dismiss"`, `"collapse"`|
|
|
449
|
+
| `backdropComponent` | Custom backdrop component; replaces default backdrop + tap handling |
|
|
450
|
+
|
|
451
|
+
### Auto Snap Points
|
|
452
|
+
|
|
453
|
+
When you use `"auto"`, the library measures the content height and exposes it in `current.layouts.content`:
|
|
454
|
+
|
|
455
|
+
```tsx
|
|
456
|
+
screenStyleInterpolator: ({ current }) => {
|
|
457
|
+
"worklet";
|
|
458
|
+
|
|
459
|
+
const contentHeight = current.layouts.content?.height ?? 0;
|
|
460
|
+
|
|
461
|
+
return {
|
|
462
|
+
content: {
|
|
463
|
+
style: {
|
|
464
|
+
opacity: interpolate(current.snapIndex, [0, 1], [0.8, 1]),
|
|
465
|
+
},
|
|
466
|
+
},
|
|
467
|
+
"sheet-height-debug": {
|
|
468
|
+
style: {
|
|
469
|
+
opacity: contentHeight > 0 ? 1 : 0,
|
|
470
|
+
},
|
|
471
|
+
},
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
#### backdropBehavior Values
|
|
477
|
+
|
|
478
|
+
| Value | Description |
|
|
479
|
+
| --------------- | ---------------------------------------------------------------- |
|
|
480
|
+
| `"block"` | Backdrop catches all touches (default) |
|
|
481
|
+
| `"passthrough"` | Touches pass through to content behind |
|
|
482
|
+
| `"dismiss"` | Tapping backdrop dismisses the screen |
|
|
483
|
+
| `"collapse"` | Tapping backdrop collapses to next lower snap point, then dismisses |
|
|
484
|
+
|
|
485
|
+
#### Custom Backdrop Component
|
|
486
|
+
|
|
487
|
+
Use `backdropComponent` when you want full control over backdrop visuals and interactions.
|
|
488
|
+
|
|
489
|
+
- When provided, it replaces the default backdrop entirely (including default tap behavior)
|
|
490
|
+
- You are responsible for dismiss/collapse actions inside the custom component
|
|
491
|
+
- `backdropBehavior` still controls container-level pointer event behavior
|
|
492
|
+
|
|
493
|
+
```tsx
|
|
494
|
+
import { router } from "expo-router";
|
|
495
|
+
import { Pressable } from "react-native";
|
|
496
|
+
import Animated, { interpolate, useAnimatedStyle } from "react-native-reanimated";
|
|
497
|
+
import { useScreenAnimation } from "react-native-screen-transitions";
|
|
498
|
+
|
|
499
|
+
function SheetBackdrop() {
|
|
500
|
+
const animation = useScreenAnimation();
|
|
501
|
+
|
|
502
|
+
const style = useAnimatedStyle(() => ({
|
|
503
|
+
opacity: interpolate(animation.value.current.progress, [0, 1], [0, 0.4]),
|
|
504
|
+
backgroundColor: "#000",
|
|
505
|
+
}));
|
|
506
|
+
|
|
507
|
+
return (
|
|
508
|
+
<Pressable style={{ flex: 1 }} onPress={() => router.back()}>
|
|
509
|
+
<Animated.View style={[{ flex: 1 }, style]} />
|
|
510
|
+
</Pressable>
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
<Stack.Screen
|
|
515
|
+
name="Sheet"
|
|
516
|
+
options={{
|
|
517
|
+
snapPoints: [0.5, 1],
|
|
518
|
+
backdropBehavior: "dismiss",
|
|
519
|
+
backdropComponent: SheetBackdrop,
|
|
520
|
+
}}
|
|
521
|
+
/>
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
### Programmatic Control
|
|
525
|
+
|
|
526
|
+
Control snap points from anywhere in your app:
|
|
527
|
+
|
|
528
|
+
```tsx
|
|
529
|
+
import { snapTo } from "react-native-screen-transitions";
|
|
530
|
+
|
|
531
|
+
function BottomSheet() {
|
|
532
|
+
// Expand to full height (index 1)
|
|
533
|
+
const expand = () => snapTo(1);
|
|
534
|
+
|
|
535
|
+
// Collapse to half height (index 0)
|
|
536
|
+
const collapse = () => snapTo(0);
|
|
537
|
+
|
|
538
|
+
return (
|
|
539
|
+
<View>
|
|
540
|
+
<Button title="Expand" onPress={expand} />
|
|
541
|
+
<Button title="Collapse" onPress={collapse} />
|
|
542
|
+
</View>
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
The animated snap point index is available via `current.snapIndex` in `ScreenInterpolationProps`:
|
|
548
|
+
|
|
549
|
+
```tsx
|
|
550
|
+
screenStyleInterpolator: ({ current }) => {
|
|
551
|
+
// snapIndex interpolates between snap point indices
|
|
552
|
+
// e.g., 0.5 means halfway between snap point 0 and 1
|
|
553
|
+
return {
|
|
554
|
+
content: {
|
|
555
|
+
style: {
|
|
556
|
+
opacity: interpolate(current.snapIndex, [0, 1], [0.5, 1]),
|
|
557
|
+
},
|
|
558
|
+
},
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
### ScrollView Behavior
|
|
564
|
+
|
|
565
|
+
With `Transition.ScrollView` inside a snap-enabled sheet:
|
|
566
|
+
- **`sheetScrollGestureBehavior: "expand-and-collapse"`**: At boundary, swipe up expands and swipe down collapses (or dismisses at min if enabled)
|
|
567
|
+
- **`sheetScrollGestureBehavior: "collapse-only"`**: Expand works only via deadspace; collapse/dismiss via scroll still works at boundary
|
|
568
|
+
- **Scrolled into content**: Normal scroll behavior
|
|
569
|
+
|
|
570
|
+
### Snap Animation Specs
|
|
571
|
+
|
|
572
|
+
Customize snap animations separately from enter/exit:
|
|
573
|
+
|
|
574
|
+
```tsx
|
|
575
|
+
transitionSpec: {
|
|
576
|
+
open: { stiffness: 1000, damping: 500, mass: 3 }, // Screen enter
|
|
577
|
+
close: { stiffness: 1000, damping: 500, mass: 3 }, // Screen exit
|
|
578
|
+
expand: { stiffness: 300, damping: 30 }, // Snap up
|
|
579
|
+
collapse: { stiffness: 300, damping: 30 }, // Snap down
|
|
580
|
+
}
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
---
|
|
584
|
+
|
|
585
|
+
## Shared Elements (Bounds API)
|
|
586
|
+
|
|
587
|
+
Animate elements between screens by tagging them. In 3.4, the recommended and forward-compatible API is `Transition.Boundary.*` for explicit bounds ownership.
|
|
588
|
+
|
|
589
|
+
`sharedBoundTag` on transition-aware components is deprecated and retained for legacy flows and presets.
|
|
590
|
+
|
|
591
|
+
### 1. Tag the Source
|
|
592
|
+
|
|
593
|
+
```tsx
|
|
594
|
+
<Transition.Boundary.Trigger
|
|
595
|
+
id="avatar"
|
|
596
|
+
onPress={() => navigation.navigate("Profile")}
|
|
597
|
+
>
|
|
598
|
+
<Image source={avatar} style={{ width: 50, height: 50 }} />
|
|
599
|
+
</Transition.Boundary.Trigger>
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
If the boundary owner is larger than the visual element you want to match,
|
|
603
|
+
wrap the measured descendant in `Transition.Boundary.Target`:
|
|
604
|
+
|
|
605
|
+
```tsx
|
|
606
|
+
<Transition.Boundary.Trigger
|
|
607
|
+
id="avatar"
|
|
608
|
+
onPress={() => navigation.navigate("Profile")}
|
|
609
|
+
style={styles.card}
|
|
610
|
+
>
|
|
611
|
+
<Transition.Boundary.Target>
|
|
612
|
+
<Image source={avatar} style={styles.image} />
|
|
613
|
+
</Transition.Boundary.Target>
|
|
614
|
+
|
|
615
|
+
<View style={styles.copy}>
|
|
616
|
+
<Text style={styles.title}>Profile</Text>
|
|
617
|
+
</View>
|
|
618
|
+
</Transition.Boundary.Trigger>
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
### 2. Tag the Destination
|
|
622
|
+
|
|
623
|
+
```tsx
|
|
624
|
+
<Transition.Boundary.View id="avatar">
|
|
625
|
+
<Image source={avatar} style={{ width: 200, height: 200 }} />
|
|
626
|
+
</Transition.Boundary.View>
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
### 3. Use in Interpolator
|
|
630
|
+
|
|
631
|
+
```tsx
|
|
632
|
+
screenStyleInterpolator: ({ bounds }) => {
|
|
633
|
+
"worklet";
|
|
634
|
+
return {
|
|
635
|
+
avatar: bounds({ id: "avatar", method: "transform" }),
|
|
636
|
+
};
|
|
637
|
+
};
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
### Navigation Zoom
|
|
641
|
+
|
|
642
|
+
For fullscreen, navigation-style shared transitions:
|
|
643
|
+
|
|
644
|
+
```tsx
|
|
645
|
+
screenStyleInterpolator: ({ bounds }) => {
|
|
646
|
+
"worklet";
|
|
647
|
+
return bounds({ id: "avatar" }).navigation.zoom({
|
|
648
|
+
target: "fullscreen",
|
|
649
|
+
});
|
|
650
|
+
};
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
### Bounds Options
|
|
654
|
+
|
|
655
|
+
| Option | Values | Description |
|
|
656
|
+
| ----------- | ---------------------------------- | ----------------------------- |
|
|
657
|
+
| `id` | string | The boundary id to match |
|
|
658
|
+
| `group` | string | Optional group key for paged/detail flows |
|
|
659
|
+
| `method` | `"transform"` `"size"` `"content"` | How to animate |
|
|
660
|
+
| `space` | `"relative"` `"absolute"` | Coordinate space |
|
|
661
|
+
| `scaleMode` | `"match"` `"none"` `"uniform"` | Aspect ratio handling |
|
|
662
|
+
| `raw` | boolean | Return raw values |
|
|
663
|
+
|
|
664
|
+
---
|
|
665
|
+
|
|
666
|
+
## Overlays
|
|
667
|
+
|
|
668
|
+
Persistent UI that animates with the stack:
|
|
669
|
+
|
|
670
|
+
```tsx
|
|
671
|
+
const TabBar = ({ focusedIndex, progress }) => {
|
|
672
|
+
const style = useAnimatedStyle(() => ({
|
|
673
|
+
transform: [{ translateY: interpolate(progress.value, [0, 1], [100, 0]) }],
|
|
674
|
+
}));
|
|
675
|
+
return <Animated.View style={[styles.tabBar, style]} />;
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
<Stack.Screen
|
|
679
|
+
name="Home"
|
|
680
|
+
options={{
|
|
681
|
+
overlay: TabBar,
|
|
682
|
+
overlayShown: true,
|
|
683
|
+
}}
|
|
684
|
+
/>
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
### Overlay Props
|
|
688
|
+
|
|
689
|
+
| Prop | Description |
|
|
690
|
+
| -------------- | ------------------------------ |
|
|
691
|
+
| `focusedRoute` | Currently focused route |
|
|
692
|
+
| `focusedIndex` | Index of focused screen |
|
|
693
|
+
| `routes` | All routes in the stack |
|
|
694
|
+
| `progress` | Stack progress (derived value) |
|
|
695
|
+
| `navigation` | Navigation prop |
|
|
696
|
+
| `meta` | Custom metadata from options |
|
|
697
|
+
|
|
698
|
+
---
|
|
699
|
+
|
|
700
|
+
## Transition Components
|
|
701
|
+
|
|
702
|
+
| Component | Description |
|
|
703
|
+
| ----------------------- | -------------------------------------- |
|
|
704
|
+
| `Transition.View` | Animated view; `sharedBoundTag` usage is legacy/deprecated |
|
|
705
|
+
| `Transition.Pressable` | Pressable; `sharedBoundTag` usage is legacy/deprecated |
|
|
706
|
+
| `Transition.ScrollView` | ScrollView with gesture coordination |
|
|
707
|
+
| `Transition.FlatList` | FlatList with gesture coordination |
|
|
708
|
+
| `Transition.Boundary.View` | Explicit bounds destination/source registration |
|
|
709
|
+
| `Transition.Boundary.Trigger` | Pressable boundary owner that captures source bounds on press |
|
|
710
|
+
| `Transition.Boundary.Target` | Optional nested measurement target inside a boundary owner |
|
|
711
|
+
| `Transition.MaskedView` | Deprecated legacy reveal helper (requires native) |
|
|
712
|
+
|
|
713
|
+
Helper exports:
|
|
714
|
+
|
|
715
|
+
- `Transition.createBoundaryComponent(Component, { alreadyAnimated?: boolean })`
|
|
716
|
+
- `Transition.createTransitionAwareComponent(Component, { isScrollable?: boolean, alreadyAnimated?: boolean })`
|
|
717
|
+
|
|
718
|
+
---
|
|
719
|
+
|
|
720
|
+
## Hooks
|
|
721
|
+
|
|
722
|
+
### useScreenAnimation
|
|
723
|
+
|
|
724
|
+
Access animation state inside a screen:
|
|
725
|
+
|
|
726
|
+
```tsx
|
|
727
|
+
import { useScreenAnimation } from "react-native-screen-transitions";
|
|
728
|
+
|
|
729
|
+
function DetailScreen() {
|
|
730
|
+
const animation = useScreenAnimation();
|
|
731
|
+
const parentAnimation = useScreenAnimation("parent");
|
|
732
|
+
|
|
733
|
+
const style = useAnimatedStyle(() => ({
|
|
734
|
+
opacity: parentAnimation.value.current.progress,
|
|
735
|
+
}));
|
|
736
|
+
|
|
737
|
+
return <Animated.View style={style}>...</Animated.View>;
|
|
738
|
+
}
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
### useScreenState
|
|
742
|
+
|
|
743
|
+
Get navigation state without animation values:
|
|
744
|
+
|
|
745
|
+
```tsx
|
|
746
|
+
import { useScreenState } from "react-native-screen-transitions";
|
|
747
|
+
|
|
748
|
+
function DetailScreen() {
|
|
749
|
+
const { index, focusedRoute, routes, navigation } = useScreenState();
|
|
750
|
+
// ...
|
|
751
|
+
}
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
### useHistory
|
|
755
|
+
|
|
756
|
+
Access navigation history across the app:
|
|
757
|
+
|
|
758
|
+
```tsx
|
|
759
|
+
import { useHistory } from "react-native-screen-transitions";
|
|
760
|
+
|
|
761
|
+
function MyComponent() {
|
|
762
|
+
const { getRecent, getPath } = useHistory();
|
|
763
|
+
|
|
764
|
+
const recentScreens = getRecent(5); // Last 5 screens
|
|
765
|
+
const path = getPath(fromKey, toKey); // Path between screens
|
|
766
|
+
}
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
### useScreenGesture
|
|
770
|
+
|
|
771
|
+
Coordinate your own pan gestures with the navigation gesture:
|
|
772
|
+
|
|
773
|
+
```tsx
|
|
774
|
+
import { useScreenGesture } from "react-native-screen-transitions";
|
|
775
|
+
import { Gesture, GestureDetector } from "react-native-gesture-handler";
|
|
776
|
+
|
|
777
|
+
function MyScreen() {
|
|
778
|
+
const screenGesture = useScreenGesture();
|
|
779
|
+
const parentGesture = useScreenGesture("parent");
|
|
780
|
+
|
|
781
|
+
const myPanGesture = Gesture.Pan()
|
|
782
|
+
.simultaneousWithExternalGesture(screenGesture, parentGesture)
|
|
783
|
+
.onUpdate((e) => {
|
|
784
|
+
// Your gesture logic
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
return (
|
|
788
|
+
<GestureDetector gesture={myPanGesture}>
|
|
789
|
+
<View />
|
|
790
|
+
</GestureDetector>
|
|
791
|
+
);
|
|
792
|
+
}
|
|
793
|
+
```
|
|
794
|
+
|
|
795
|
+
Use this when you have custom pan gestures that need to work alongside screen dismiss gestures.
|
|
796
|
+
You can target `"self"`, `"parent"`, `"root"`, or `{ ancestor: number }`.
|
|
797
|
+
|
|
798
|
+
---
|
|
799
|
+
|
|
800
|
+
## Advanced Animation Props
|
|
801
|
+
|
|
802
|
+
The full `screenStyleInterpolator` receives these props:
|
|
803
|
+
|
|
804
|
+
| Prop | Description |
|
|
805
|
+
| ---------------- | -------------------------------------------------------- |
|
|
806
|
+
| `progress` | Combined progress (0-2) |
|
|
807
|
+
| `stackProgress` | Accumulated progress across entire stack |
|
|
808
|
+
| `focused` | Whether this screen is the topmost in the stack |
|
|
809
|
+
| `current` | Current screen state |
|
|
810
|
+
| `previous` | Previous screen state |
|
|
811
|
+
| `next` | Next screen state |
|
|
812
|
+
| `active` | Screen driving the transition |
|
|
813
|
+
| `inactive` | Screen NOT driving the transition |
|
|
814
|
+
| `insets` | Safe area insets |
|
|
815
|
+
| `bounds` | Shared element bounds function |
|
|
816
|
+
|
|
817
|
+
Prefer `current.snapIndex`, `current.layouts.screen`, and `current.layouts.content` for new code.
|
|
818
|
+
|
|
819
|
+
### Screen State Properties
|
|
820
|
+
|
|
821
|
+
Each screen state (`current`, `previous`, `next`, `active`, `inactive`) contains:
|
|
822
|
+
|
|
823
|
+
| Property | Description |
|
|
824
|
+
| ----------- | ---------------------------------------- |
|
|
825
|
+
| `progress` | Animation progress (0 or 1) |
|
|
826
|
+
| `closing` | Whether closing (0 or 1) |
|
|
827
|
+
| `entering` | Whether entering (0 or 1) |
|
|
828
|
+
| `animating` | Whether animating (0 or 1) |
|
|
829
|
+
| `logicallySettled` | Whether choreography can treat the screen as done |
|
|
830
|
+
| `snapIndex` | Animated snap point index for this screen |
|
|
831
|
+
| `layouts` | Screen and measured content layouts |
|
|
832
|
+
| `gesture` | Gesture values (x, y, `normX`, `normY`, etc.) |
|
|
833
|
+
| `meta` | Custom metadata from options |
|
|
834
|
+
|
|
835
|
+
### Using `meta` for Conditional Logic
|
|
836
|
+
|
|
837
|
+
Pass custom data between screens:
|
|
838
|
+
|
|
839
|
+
```tsx
|
|
840
|
+
// Screen A
|
|
841
|
+
options={{ meta: { hideTabBar: true } }}
|
|
842
|
+
|
|
843
|
+
// Screen B reads it
|
|
844
|
+
screenStyleInterpolator: (props) => {
|
|
845
|
+
"worklet";
|
|
846
|
+
const hideTabBar = props.inactive?.meta?.hideTabBar;
|
|
847
|
+
// ...
|
|
848
|
+
};
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
### Animate Individual Elements
|
|
852
|
+
|
|
853
|
+
Use `styleId` to target specific elements:
|
|
854
|
+
|
|
855
|
+
```tsx
|
|
856
|
+
// In options
|
|
857
|
+
screenStyleInterpolator: ({ progress }) => {
|
|
858
|
+
"worklet";
|
|
859
|
+
return {
|
|
860
|
+
"hero-image": {
|
|
861
|
+
style: {
|
|
862
|
+
opacity: interpolate(progress, [0, 1], [0, 1]),
|
|
863
|
+
},
|
|
864
|
+
},
|
|
865
|
+
};
|
|
866
|
+
};
|
|
867
|
+
|
|
868
|
+
// In component
|
|
869
|
+
<Transition.View styleId="hero-image">
|
|
870
|
+
<Image source={...} />
|
|
871
|
+
</Transition.View>
|
|
872
|
+
```
|
|
873
|
+
|
|
874
|
+
---
|
|
875
|
+
|
|
876
|
+
## Stack Types
|
|
877
|
+
|
|
878
|
+
Blank stack and native stack are the primary APIs. Component stack remains available as a deprecated compatibility API.
|
|
879
|
+
|
|
880
|
+
| Stack | Best For |
|
|
881
|
+
| ------------------- | --------------------------------------------------------- |
|
|
882
|
+
| **Blank Stack** | Most apps. Full control, all features. |
|
|
883
|
+
| **Native Stack** | When you need native screen primitives. |
|
|
884
|
+
| **Component Stack** | Legacy embedded-flow API. Prefer blank stack instead. |
|
|
885
|
+
|
|
886
|
+
### Blank Stack
|
|
887
|
+
|
|
888
|
+
The default choice. Uses `react-native-screens` for native screen containers, with animations powered by Reanimated worklets running on the UI thread (not the JS thread).
|
|
889
|
+
|
|
890
|
+
```tsx
|
|
891
|
+
import { createBlankStackNavigator } from "react-native-screen-transitions/blank-stack";
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
### Native Stack
|
|
895
|
+
|
|
896
|
+
Extends `@react-navigation/native-stack`. Requires `enableTransitions: true`.
|
|
897
|
+
|
|
898
|
+
```tsx
|
|
899
|
+
import { createNativeStackNavigator } from "react-native-screen-transitions/native-stack";
|
|
900
|
+
|
|
901
|
+
<Stack.Screen
|
|
902
|
+
name="Detail"
|
|
903
|
+
options={{
|
|
904
|
+
enableTransitions: true,
|
|
905
|
+
...Transition.Presets.SlideFromBottom(),
|
|
906
|
+
}}
|
|
907
|
+
/>
|
|
908
|
+
```
|
|
909
|
+
|
|
910
|
+
### Component Stack (Deprecated)
|
|
911
|
+
|
|
912
|
+
> **Note:** Prefer blank stack for new work. It now covers the embedded and independent flow use case.
|
|
913
|
+
|
|
914
|
+
Standalone navigator, not connected to React Navigation. Kept for compatibility with older integrations.
|
|
915
|
+
|
|
916
|
+
```tsx
|
|
917
|
+
import { createComponentStackNavigator } from "react-native-screen-transitions/component-stack";
|
|
918
|
+
|
|
919
|
+
const Stack = createComponentStackNavigator();
|
|
920
|
+
|
|
921
|
+
<Stack.Navigator initialRouteName="step1">
|
|
922
|
+
<Stack.Screen name="step1" component={Step1} />
|
|
923
|
+
<Stack.Screen name="step2" component={Step2} />
|
|
924
|
+
</Stack.Navigator>
|
|
925
|
+
```
|
|
926
|
+
|
|
927
|
+
---
|
|
928
|
+
|
|
929
|
+
## Caveats & Trade-offs
|
|
930
|
+
|
|
931
|
+
### Native Stack
|
|
932
|
+
|
|
933
|
+
The Native Stack uses transparent modal presentation to intercept transitions. This has trade-offs:
|
|
934
|
+
|
|
935
|
+
- **Delayed touch events** – Exiting screens may have briefly delayed touch response
|
|
936
|
+
- **beforeRemove listeners** – Relies on navigation lifecycle events
|
|
937
|
+
- **Rapid navigation** – Some edge cases with very fast navigation sequences
|
|
938
|
+
|
|
939
|
+
For most apps, Blank Stack avoids these issues entirely.
|
|
940
|
+
|
|
941
|
+
### Component Stack (Experimental)
|
|
942
|
+
|
|
943
|
+
- **No deep linking** – Routes aren't part of your URL structure
|
|
944
|
+
- **Isolated state** – Doesn't affect parent navigation
|
|
945
|
+
- **Touch pass-through** – Uses `pointerEvents="box-none"` by default
|
|
946
|
+
|
|
947
|
+
---
|
|
948
|
+
|
|
949
|
+
## Experimental Features
|
|
950
|
+
|
|
951
|
+
### High Refresh Rate
|
|
952
|
+
|
|
953
|
+
Force maximum refresh rate during transitions (for 90Hz/120Hz displays):
|
|
954
|
+
|
|
955
|
+
```tsx
|
|
956
|
+
options={{
|
|
957
|
+
experimental_enableHighRefreshRate: true,
|
|
958
|
+
}}
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
### Animate On Initial Mount
|
|
962
|
+
|
|
963
|
+
Animate the first screen in a navigator instead of snapping it directly to the settled state:
|
|
964
|
+
|
|
965
|
+
```tsx
|
|
966
|
+
options={{
|
|
967
|
+
experimental_animateOnInitialMount: true,
|
|
968
|
+
}}
|
|
969
|
+
```
|
|
970
|
+
|
|
971
|
+
---
|
|
972
|
+
|
|
973
|
+
## Legacy Masked View Setup (Deprecated)
|
|
974
|
+
|
|
975
|
+
`Transition.MaskedView` and `sharedBoundTag` are deprecated for new work.
|
|
976
|
+
|
|
977
|
+
Prefer `Transition.Boundary.*` for explicit shared-element ownership, and prefer `bounds({ id }).navigation.zoom()` with `navigationMaskEnabled` when you want library-managed navigation-style masked transitions.
|
|
978
|
+
|
|
979
|
+
This section is kept for compatibility with legacy presets such as `SharedIGImage` and `SharedAppleMusic`.
|
|
980
|
+
|
|
981
|
+
> **Note**: Requires native code. Will not work in Expo Go.
|
|
982
|
+
|
|
983
|
+
### Installation
|
|
984
|
+
|
|
985
|
+
```bash
|
|
986
|
+
# Expo
|
|
987
|
+
npx expo install @react-native-masked-view/masked-view
|
|
988
|
+
|
|
989
|
+
# Bare React Native
|
|
990
|
+
npm install @react-native-masked-view/masked-view
|
|
991
|
+
cd ios && pod install
|
|
992
|
+
```
|
|
993
|
+
|
|
994
|
+
### Full Example
|
|
995
|
+
|
|
996
|
+
**1. Source Screen** – Tag pressable elements:
|
|
997
|
+
|
|
998
|
+
```tsx
|
|
999
|
+
// app/index.tsx
|
|
1000
|
+
import { router } from "expo-router";
|
|
1001
|
+
import { View } from "react-native";
|
|
1002
|
+
import Transition from "react-native-screen-transitions";
|
|
1003
|
+
|
|
1004
|
+
export default function HomeScreen() {
|
|
1005
|
+
return (
|
|
1006
|
+
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
|
|
1007
|
+
<Transition.Pressable
|
|
1008
|
+
sharedBoundTag="album-art"
|
|
1009
|
+
style={{
|
|
1010
|
+
width: 200,
|
|
1011
|
+
height: 200,
|
|
1012
|
+
backgroundColor: "#1DB954",
|
|
1013
|
+
borderRadius: 12,
|
|
1014
|
+
}}
|
|
1015
|
+
onPress={() => {
|
|
1016
|
+
router.push({
|
|
1017
|
+
pathname: "/details",
|
|
1018
|
+
params: { sharedBoundTag: "album-art" },
|
|
1019
|
+
});
|
|
1020
|
+
}}
|
|
1021
|
+
/>
|
|
1022
|
+
</View>
|
|
1023
|
+
);
|
|
1024
|
+
}
|
|
1025
|
+
```
|
|
1026
|
+
|
|
1027
|
+
**2. Destination Screen** – Wrap with MaskedView and match the tag:
|
|
1028
|
+
|
|
1029
|
+
```tsx
|
|
1030
|
+
// app/details.tsx
|
|
1031
|
+
import { useLocalSearchParams } from "expo-router";
|
|
1032
|
+
import Transition from "react-native-screen-transitions";
|
|
1033
|
+
|
|
1034
|
+
export default function DetailsScreen() {
|
|
1035
|
+
const { sharedBoundTag } = useLocalSearchParams<{ sharedBoundTag: string }>();
|
|
1036
|
+
|
|
1037
|
+
return (
|
|
1038
|
+
<Transition.MaskedView style={{ flex: 1, backgroundColor: "#121212" }}>
|
|
1039
|
+
<Transition.View
|
|
1040
|
+
sharedBoundTag={sharedBoundTag}
|
|
1041
|
+
style={{
|
|
1042
|
+
backgroundColor: "#1DB954",
|
|
1043
|
+
width: 400,
|
|
1044
|
+
height: 400,
|
|
1045
|
+
alignSelf: "center",
|
|
1046
|
+
borderRadius: 12,
|
|
1047
|
+
}}
|
|
1048
|
+
/>
|
|
1049
|
+
{/* Additional screen content */}
|
|
1050
|
+
</Transition.MaskedView>
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
1053
|
+
```
|
|
1054
|
+
|
|
1055
|
+
**3. Layout** – Apply the preset with dynamic tag:
|
|
1056
|
+
|
|
1057
|
+
```tsx
|
|
1058
|
+
// app/_layout.tsx
|
|
1059
|
+
import Transition from "react-native-screen-transitions";
|
|
1060
|
+
import { Stack } from "./stack";
|
|
1061
|
+
|
|
1062
|
+
export default function RootLayout() {
|
|
1063
|
+
return (
|
|
1064
|
+
<Stack>
|
|
1065
|
+
<Stack.Screen name="index" />
|
|
1066
|
+
<Stack.Screen
|
|
1067
|
+
name="details"
|
|
1068
|
+
options={({ route }) => ({
|
|
1069
|
+
...Transition.Presets.SharedAppleMusic({
|
|
1070
|
+
sharedBoundTag: route.params?.sharedBoundTag ?? "",
|
|
1071
|
+
}),
|
|
1072
|
+
})}
|
|
1073
|
+
/>
|
|
1074
|
+
</Stack>
|
|
1075
|
+
);
|
|
1076
|
+
}
|
|
1077
|
+
```
|
|
1078
|
+
|
|
1079
|
+
### How It Works
|
|
1080
|
+
|
|
1081
|
+
1. `Transition.Pressable` measures its bounds on press and stores them with the tag
|
|
1082
|
+
2. `Transition.View` on the destination registers as the target for that tag
|
|
1083
|
+
3. `Transition.MaskedView` clips content to the animating shared element bounds
|
|
1084
|
+
4. The preset interpolates position, size, and mask for a seamless expand/collapse effect
|
|
1085
|
+
|
|
1086
|
+
---
|
|
1087
|
+
|
|
1088
|
+
## Support
|
|
1089
|
+
|
|
1090
|
+
This package is developed in my spare time.
|
|
1091
|
+
|
|
1092
|
+
If you'd like to fuel the next release, [buy me a coffee](https://buymeacoffee.com/trpfsu)
|
|
1093
|
+
|
|
1094
|
+
## License
|
|
45
1095
|
|
|
46
|
-
|
|
47
|
-
- Stable docs line: `3.x`
|
|
48
|
-
- Unreleased docs line: `Next`
|
|
49
|
-
- Repository: `https://github.com/eds2002/react-native-screen-transitions`
|
|
1096
|
+
MIT
|