Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add screen transition animation #5274

Merged
merged 58 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from 57 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
ef35469
Copy changes from rn-screens
piaskowyk Oct 19, 2023
0329270
Call configure props
piaskowyk Oct 19, 2023
97ec16b
Add example
piaskowyk Oct 19, 2023
a5fc158
Export types
piaskowyk Oct 19, 2023
803c380
Use Screen's methods on UI
piaskowyk Oct 20, 2023
c74789d
Update package.json
piaskowyk Oct 20, 2023
a0cd9e1
Run Android on UI
piaskowyk Oct 25, 2023
5b372dc
Added easing
piaskowyk Oct 26, 2023
d9636ac
Update screens version
piaskowyk Oct 26, 2023
68c6493
Plit update
piaskowyk Oct 26, 2023
b2ff069
Merge branch 'main' into @piaskowyk/screen-animations-swipe-back
piaskowyk Nov 2, 2023
f975ef0
Run format
piaskowyk Nov 2, 2023
4ed7262
Use screens as local tarball
piaskowyk Nov 3, 2023
1732b3d
Compatibility without screens
piaskowyk Nov 6, 2023
a6c90ae
Update example
piaskowyk Nov 6, 2023
8c71ce3
Update hash
piaskowyk Nov 6, 2023
7a0f14c
Web mock
piaskowyk Nov 6, 2023
984afad
Web mock
piaskowyk Nov 6, 2023
e3f60c8
Force common interface
piaskowyk Nov 6, 2023
115f4e6
Update hash
piaskowyk Nov 6, 2023
3fd934a
Update hash again
piaskowyk Nov 6, 2023
de25c01
Update gradle properties
piaskowyk Nov 6, 2023
45f86f7
Update globals
piaskowyk Nov 6, 2023
0858237
Update gesture simulator
piaskowyk Nov 13, 2023
34385be
Update dependencies
piaskowyk Nov 13, 2023
0880b7d
Update screens
piaskowyk Nov 14, 2023
7f6fd8e
Add use strict
piaskowyk Nov 14, 2023
5874b31
update example
piaskowyk Nov 14, 2023
6dc6d95
Merge branch 'main' into @piaskowyk/screen-animations-swipe-back
piaskowyk Dec 1, 2023
20685b8
Improvements after review
piaskowyk Dec 5, 2023
7513d1e
Java lint
piaskowyk Dec 5, 2023
5c6ca32
Align to the new screens interface
piaskowyk Dec 5, 2023
4031de8
Remove dependency for RNScreensModule
piaskowyk Dec 12, 2023
39d52d1
Add worklet directive
piaskowyk Dec 12, 2023
5282eb9
Lint Java
piaskowyk Dec 12, 2023
9aa642b
Clean up
piaskowyk Dec 12, 2023
1e0c321
Update types
piaskowyk Dec 13, 2023
2711ed4
Update example app
piaskowyk Dec 13, 2023
a68c1fc
Remove screes .tgz
piaskowyk Dec 13, 2023
f3136f8
Update example
piaskowyk Dec 13, 2023
f3b1619
Merge branch 'main' into @piaskowyk/screen-animations-swipe-back
piaskowyk Dec 13, 2023
7c7bb5a
Merge branch 'main' into @piaskowyk/screen-animations-swipe-back
piaskowyk Dec 28, 2023
5a7d165
Review
piaskowyk Dec 28, 2023
a8fac50
Restore file name
piaskowyk Dec 28, 2023
1a5c16f
ScreensTurboModule mock
piaskowyk Dec 28, 2023
cac050d
Small refactor
piaskowyk Dec 28, 2023
9e2c337
Merge branch 'main' into @piaskowyk/screen-animations-swipe-back
piaskowyk Jan 9, 2024
9d9cf44
Merge branch 'main' into @piaskowyk/screen-animations-swipe-back
piaskowyk Feb 27, 2024
c272b60
Improvements after review part 1
piaskowyk Mar 4, 2024
218cdd5
Update name
piaskowyk Mar 5, 2024
8059ffa
Replace function syntax
piaskowyk Mar 6, 2024
f22e6cc
Restore below top screen state
piaskowyk Mar 6, 2024
4dde3f8
Don't copy the event
piaskowyk Mar 7, 2024
880870d
Run formatter
piaskowyk Mar 7, 2024
e852d6c
Remove unused export
piaskowyk Mar 7, 2024
2cbeff6
Restore changes
piaskowyk Mar 7, 2024
53b4e38
Merge branch 'main' into @piaskowyk/screen-animations-swipe-back
piaskowyk Mar 18, 2024
bffb073
Merge branch 'main' into @piaskowyk/screen-animations-swipe-back
piaskowyk Mar 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions app/src/examples/ScreenTransitionExample.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// @ts-nocheck - It will be removed after release of react-native-screens, because currently 'react-native-screens/gesture-handler' import doesn't exist.
import React from 'react';
import { View, StyleSheet, Button } from 'react-native';
import {
createNativeStackNavigator,
NativeStackScreenProps,
} from 'react-native-screens/native-stack';
import { GestureDetectorProvider } from 'react-native-screens/gesture-handler';
import { ParamListBase } from '@react-navigation/native';
import {
ScreenTransition,
AnimatedScreenTransition,
} from 'react-native-reanimated';

function MainScreen({ navigation }: NativeStackScreenProps<ParamListBase>) {
return (
<View style={[styles.container, styles.screenA]}>
<Button
title="Go ScreenB"
onPress={() => navigation.navigate('ScreenB')}
/>
<Button onPress={() => navigation.pop()} title="🔙 Back to Examples" />
</View>
);
}

function ScreenB({ navigation }: NativeStackScreenProps<ParamListBase>) {
return (
<View style={[styles.container, styles.screenB]}>
<Button
title="Go ScreenC"
onPress={() => navigation.navigate('ScreenC')}
/>
<Button title="Go back" onPress={() => navigation.goBack()} />
</View>
);
}

function ScreenC({ navigation }: NativeStackScreenProps<ParamListBase>) {
return (
<View style={[styles.container, styles.screenC]}>
<Button title="Go back" onPress={() => navigation.goBack()} />
</View>
);
}

const Stack = createNativeStackNavigator();

const customTransition: AnimatedScreenTransition = {
topScreenFrame: (event, screenSize) => {
'worklet';
const progress = event.translationX / screenSize.width;
return {
transform: [
{ translateX: event.translationX },
{ rotate: `${20 * progress}deg` },
],
};
},
belowTopScreenFrame: (event, screenSize) => {
'worklet';
const progress = event.translationX / screenSize.width;
return {
transform: [{ scale: 0.7 + 0.3 * progress }],
};
},
};

function ScreenTransitionExample(): JSX.Element {
return (
<GestureDetectorProvider>
<Stack.Navigator
screenOptions={{
headerHideBackButton: true,
stackAnimation: 'none',
goBackGesture: 'swipeRight',
}}>
<Stack.Screen name="ScreenA" component={MainScreen} />
<Stack.Screen
name="ScreenB"
component={ScreenB}
options={{ transitionAnimation: customTransition }}
/>
<Stack.Screen
name="ScreenC"
component={ScreenC}
options={{ transitionAnimation: ScreenTransition.SwipeRightFade }}
/>
</Stack.Navigator>
</GestureDetectorProvider>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 10,
},
screenA: {
backgroundColor: 'moccasin',
},
screenB: {
backgroundColor: 'thistle',
},
screenC: {
backgroundColor: 'blue',
},
});

export default ScreenTransitionExample;
19 changes: 19 additions & 0 deletions app/src/examples/ScreenTransitionExample.web.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { StyleSheet, Text, View } from 'react-native';

import React from 'react';

export default function ScreenTransitionExample() {
return (
<View style={styles.container}>
<Text>Screen transition example is not supported on web</Text>
</View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
});
7 changes: 6 additions & 1 deletion app/src/examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ import WobbleExample from './WobbleExample';
import WorkletExample from './WorkletExample';
import WorkletRuntimeExample from './WorkletRuntimeExample';
import NestedLayoutAnimationConfig from './LayoutAnimations/NestedLayoutAnimationConfig';
// import ScreenTransitionExample from './ScreenTransitionExample';
import WithClampExample from './WithClampExample';
import WorkletFactoryCrash from './WorkletFactoryCrashExample';
import RuntimeTestsExample from './RuntimeTests/RuntimeTestsExample';
Expand All @@ -130,7 +131,6 @@ interface Example {

export const EXAMPLES: Record<string, Example> = {
// Empty example for test purposes

EmptyExample: {
icon: '👻',
title: 'Empty',
Expand Down Expand Up @@ -237,6 +237,11 @@ export const EXAMPLES: Record<string, Example> = {
title: 'Update props performance',
screen: UpdatePropsPerfExample,
},
// ScreenTransitionExample: {
// icon: '📺',
// title: 'Screen transition',
// screen: ScreenTransitionExample,
// },
piaskowyk marked this conversation as resolved.
Show resolved Hide resolved

// Basic examples

Expand Down
2 changes: 1 addition & 1 deletion src/ConfigHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ function assertNoOverlapInLists() {
}
}

function configureProps(): void {
export function configureProps(): void {
assertNoOverlapInLists();
jsiConfigureProps(
Object.keys(PropsAllowlists.UI_THREAD_PROPS_WHITELIST),
Expand Down
2 changes: 2 additions & 0 deletions src/reanimated2/globals.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import type { ProgressTransitionRegister } from './layoutReanimation/sharedTrans
import type { UpdatePropsManager } from './UpdateProps';
import type { callGuardDEV } from './initializers';
import type { WorkletRuntime } from './runtimes';
import type { RNScreensTurboModuleType } from './screenTransition/commonTypes';

declare global {
var _REANIMATED_IS_REDUCED_MOTION: boolean | undefined;
Expand Down Expand Up @@ -102,6 +103,7 @@ declare global {
var UpdatePropsManager: UpdatePropsManager;
var ProgressTransitionRegister: ProgressTransitionRegister;
var updateJSProps: (viewTag: number, props: Record<string, unknown>) => void;
var RNScreensTurboModule: RNScreensTurboModuleType | undefined;
var _obtainPropPaper: (viewTag: number, propName: string) => string;
var _obtainPropFabric: (
shadowNodeWrapper: ShadowNodeWrapper,
Expand Down
10 changes: 10 additions & 0 deletions src/reanimated2/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,3 +273,13 @@ export type {
export type { AnimatedScrollViewProps } from './component/ScrollView';
export type { FlatListPropsWithLayout } from './component/FlatList';
export { startMapper, stopMapper } from './mappers';
export {
startScreenTransition,
finishScreenTransition,
ScreenTransition,
} from './screenTransition';
export type {
AnimatedScreenTransition,
GoBackGesture,
ScreenTransitionConfig,
} from './screenTransition';
29 changes: 29 additions & 0 deletions src/reanimated2/screenTransition/RNScreensTurboModule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use strict';
import type { RNScreensTurboModuleType } from './commonTypes';

function noopFactory<T>(defaultReturnValue?: T): () => T {
return () => {
'worklet';
console.warn(
'[Reanimated] RNScreensTurboModule has not been found. Check that you have installed `[email protected]` or newer in your project and rebuilt your app.'
);
return defaultReturnValue as T;
};
}

type TransactionConfig = {
topScreenId: number;
belowTopScreenId: number;
canStartTransition: boolean;
};

export const RNScreensTurboModule: RNScreensTurboModuleType =
global.RNScreensTurboModule || {
startTransition: noopFactory<TransactionConfig>({
topScreenId: -1,
belowTopScreenId: -1,
canStartTransition: false,
}),
updateTransition: noopFactory(),
finishTransition: noopFactory(),
};
45 changes: 45 additions & 0 deletions src/reanimated2/screenTransition/animationManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
'use strict';

import type { LockAxis, ScreenTransitionConfig } from './commonTypes';
import { configureProps } from '../../ConfigHelper';
import { applyStyle } from './styleUpdater';
import { getSwipeSimulator } from './swipeSimulator';

configureProps();
piaskowyk marked this conversation as resolved.
Show resolved Hide resolved

export function startScreenTransition(
screenTransitionConfig: ScreenTransitionConfig
) {
'worklet';
const { stackTag, sharedEvent } = screenTransitionConfig;
sharedEvent.addListener(stackTag, () => {
applyStyle(screenTransitionConfig, sharedEvent.value);
});
}

function getLockAxis(goBackGesture: string): LockAxis {
'worklet';
piaskowyk marked this conversation as resolved.
Show resolved Hide resolved
if (['swipeRight', 'swipeLeft', 'horizontalSwipe'].includes(goBackGesture)) {
return 'x';
} else if (
['swipeUp', 'swipeDown', 'verticalSwipe'].includes(goBackGesture)
) {
return 'y';
}
return undefined;
}

export function finishScreenTransition(
screenTransitionConfig: ScreenTransitionConfig
) {
'worklet';
const { stackTag, sharedEvent, goBackGesture } = screenTransitionConfig;
sharedEvent.removeListener(stackTag);
const lockAxis = getLockAxis(goBackGesture);
const step = getSwipeSimulator(
sharedEvent.value,
screenTransitionConfig,
lockAxis
);
step();
}
63 changes: 63 additions & 0 deletions src/reanimated2/screenTransition/commonTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
'use strict';

import type {
MeasuredDimensions,
ShadowNodeWrapper,
SharedValue,
} from '../commonTypes';

export type PanGestureHandlerEventPayload = {
x: number;
y: number;
absoluteX: number;
absoluteY: number;
translationX: number;
translationY: number;
velocityX: number;
velocityY: number;
};

export type AnimatedScreenTransition = {
topScreenFrame: (
event: PanGestureHandlerEventPayload,
screenDimensions: MeasuredDimensions
) => Record<string, unknown>;
belowTopScreenFrame: (
event: PanGestureHandlerEventPayload,
screenDimensions: MeasuredDimensions
) => Record<string, unknown>;
};

export type GoBackGesture =
| 'swipeRight'
| 'swipeLeft'
| 'swipeUp'
| 'swipeDown'
| 'verticalSwipe'
| 'horizontalSwipe'
| 'twoDimensionalSwipe';

export type ScreenTransitionConfig = {
stackTag: number;
belowTopScreenId: number | ShadowNodeWrapper;
topScreenId: number | ShadowNodeWrapper;
screenTransition: AnimatedScreenTransition;
sharedEvent: SharedValue<PanGestureHandlerEventPayload>;
startingGesturePosition: SharedValue<PanGestureHandlerEventPayload>;
onFinishAnimation?: () => void;
isTransitionCanceled: boolean;
piaskowyk marked this conversation as resolved.
Show resolved Hide resolved
goBackGesture: GoBackGesture;
screenDimensions: MeasuredDimensions;
};

export type RNScreensTurboModuleType = {
startTransition: (stackTag: number) => {
topScreenId: number | ShadowNodeWrapper;
belowTopScreenId: number | ShadowNodeWrapper;
canStartTransition: boolean;
};
piaskowyk marked this conversation as resolved.
Show resolved Hide resolved
updateTransition: (stackTag: number, progress: number) => void;
finishTransition: (stackTag: number, isCanceled: boolean) => void;
piaskowyk marked this conversation as resolved.
Show resolved Hide resolved
};

export type LockAxis = 'x' | 'y' | undefined;
12 changes: 12 additions & 0 deletions src/reanimated2/screenTransition/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
'use strict';

export {
startScreenTransition,
finishScreenTransition,
} from './animationManager';
export { ScreenTransition } from './presets';
export type {
AnimatedScreenTransition,
GoBackGesture,
ScreenTransitionConfig,
} from './commonTypes';
Loading