software-mansion / react-native-reanimated

React Native's Animated library reimplemented
https://docs.swmansion.com/react-native-reanimated/
MIT License
8.96k stars 1.3k forks source link

Shared Element Transition not Working With Latest React Native #6630

Open aymather opened 3 days ago

aymather commented 3 days ago

Description

I'm trying to master the concept of using shared element transitions. I've set up a very basic example which animates a view with some standard properties to animate. Then I added the layer of custom animations. I am not doing anything fancy in the custom animation, I essentially just wanted to replicate the standard behavior with a custom animation to see how it works.

Here's my example. All I have is a button that navigates to a different page, and I animate an Animated.View as a shared element between the two pages.

import { Text, View } from '@atomic';
import { Button } from '@molecules';
import {
    NavigationContainer,
    RouteProp,
    useNavigation
} from '@react-navigation/native';
import {
    createNativeStackNavigator,
    NativeStackNavigationProp
} from '@react-navigation/native-stack';
import { StackNavigationProp } from '@react-navigation/stack';
import React from 'react';
import { StyleSheet } from 'react-native';
import Animated, {
    SharedTransition,
    withSpring
} from 'react-native-reanimated';

export const Stack = createNativeStackNavigator();

const bouncyTransition = SharedTransition.custom((values) => {
    'worklet';
    return {
        height: withSpring(values.targetHeight),
        width: withSpring(values.targetWidth),
        originX: withSpring(values.targetOriginX),
        originY: withSpring(values.targetOriginY),
        borderRadius: withSpring(values.targetBorderRadius)
    };
});

export type RootStackParamList = {
    HomePage: undefined;
    DetailsPage: undefined;
};

export type HomeScreenNavigationProp = StackNavigationProp<
    RootStackParamList,
    'HomePage'
>;
export type DetailScreenNavigationProp = StackNavigationProp<
    RootStackParamList,
    'DetailsPage'
>;
export type DetailScreenRouteProp = RouteProp<
    RootStackParamList,
    'DetailsPage'
>;

const DetailsPage = () => {
    const navigation =
        useNavigation<NativeStackNavigationProp<RootStackParamList>>();
    const back = () => {
        navigation.goBack();
    };
    return (
        <View flex={1} justifyContent='center'>
            <Text>Details Page</Text>
            <Button text='Back' onPress={back} />
            <Animated.View
                sharedTransitionTag='box'
                sharedTransitionStyle={bouncyTransition}
                style={styles.box2}
            />
        </View>
    );
};

const HomePage = () => {
    const navigation =
        useNavigation<NativeStackNavigationProp<RootStackParamList>>();
    const goToDetails = () => {
        navigation.navigate('DetailsPage');
    };
    return (
        <View flex={1} justifyContent='center' alignItems='center'>
            <Text>Home Page</Text>
            <Button text='Details' onPress={goToDetails} />
            <Animated.View
                sharedTransitionTag='box'
                sharedTransitionStyle={bouncyTransition}
                style={styles.box1}
            />
        </View>
    );
};

const styles = StyleSheet.create({
    box1: {
        backgroundColor: 'red',
        borderRadius: 5,
        height: 100,
        width: 100
    },
    box2: {
        backgroundColor: 'red',
        borderRadius: 50,
        height: 200,
        width: 200
    }
});

const TransitionMaster = () => {
    return (
        <NavigationContainer>
            <Stack.Navigator>
                <Stack.Screen name='HomePage' component={HomePage} />
                <Stack.Screen
                    name='DetailsPage'
                    component={DetailsPage}
                    options={{
                        presentation: 'transparentModal',
                        animation: 'fade'
                    }}
                />
            </Stack.Navigator>
        </NavigationContainer>
    );
};

export default TransitionMaster;

https://github.com/user-attachments/assets/c1845874-b41b-42bf-b635-a645545505b9

All of this comes from my current project where I was playing around with this. When I tried to reproduce this in a minimal repo, I installed all the latest react native things, and shared transitions weren't even working at all.

https://github.com/user-attachments/assets/6eb03c8e-a907-485f-b106-0364b36d89cf

The example I'm going to provide is to address the fact that it is not working at all with the latest react native.

Steps to reproduce

  1. Install & configure react native, react navigation, react native reanimated
  2. Set up a basic shared element transition on an Animate.View

Snack or a link to a repository

https://github.com/aymather/custom-shared-transitions

Reanimated version

^3.16.1

React Native version

0.76.0

Platforms

iOS

JavaScript runtime

Hermes

Workflow

React Native

Architecture

Fabric (New Architecture)

Build type

Debug app & dev bundle

Device

iOS simulator

Device model

iPhone 15 Plus

Acknowledgements

Yes

bartlomiejbloniarz commented 2 days ago

Hi @aymather! Shared Elements Transitions are not yet implemented on the New Architecture, which became the default in RN 0.76.

aymather commented 4 hours ago

@bartlomiejbloniarz Oh ok. What about the existing architecture though? Seems like it still isn't stable (per the first video in my post). I could maybe make a minimal repo to reproduce that but what I posted is like the most basic usage of custom transitions. The documentation does seem to allude to the idea that shared transitions aren't exactly super supported yet, but I guess it'd be helpful for us all to know where that's at?