IjzerenHein / react-navigation-shared-element

React Navigation bindings for react-native-shared-element 💫
https://github.com/IjzerenHein/react-native-shared-element
MIT License
1.27k stars 124 forks source link

SharedElement is stuck in ScrollView #147

Open YisusMB opened 3 years ago

YisusMB commented 3 years ago

When go back from a details screen the image wrapped inside SharedElement got stuck in the screen, here's a video:

https://user-images.githubusercontent.com/43631579/108575576-58069800-72e0-11eb-97f7-ffe6e9005160.mov

And this is my code of ScrollView:

<ScrollView style={{ flex: 1, paddingHorizontal: 20, marginTop: 15 }}>
        {meals.map(meal => {
          return (
            <>
              <Text style={{ color: 'white', fontSize: 25 }}>{meal.type}</Text>
              <LinearGradient colors={COLOR_GRADIENT_BUTTON} style={{ width: '100%', height: 255, borderRadius: 15, marginVertical: 15 }}>
                <TouchableOpacity activeOpacity={1} onPress={() => navigation.navigate('Recipe', { item: meal })}>
                  <SharedElement id={meal.id}>
                    <Image
                      style={{ borderTopLeftRadius: 15, borderTopRightRadius: 15 }}
                      source={{
                        uri: meal.image,
                        width: width - 20,
                        height: 200,
                      }}
                    />
                  </SharedElement>
                </TouchableOpacity>
                <Text style={{ fontSize: 15, color: 'white', marginTop: 10, paddingHorizontal: 5 }}>{meal.title}</Text>
                <Text style={{ fontSize: 10, color: 'white', marginTop: 5, paddingHorizontal: 5 }}>{meal.kcal}kcal</Text>
              </LinearGradient>
            </>
          )
        })}
      </ScrollView>

and code in the DetailsScreen

const RecipeScreen = ({ navigation }) => {
  const { width } = Dimensions.get('window')
  const item = navigation.getParam('item')
  return (
    <SafeAreaView style={{ flex: 1, backgroundColor: BACKGROUND_PRIMARY }}>
      <Header backButton navigation={navigation} customBack={() => navigation.goBack()} />
      <View style={{ height: 200, width: '100%', marginTop: hp('5.5%') }}>
        <SharedElement id={item.id}>
          <Image
            style={{ borderTopLeftRadius: 15, borderTopRightRadius: 15 }}
            source={{
              uri: item.image,
              width,
              height: 200,
            }}
          />
        </SharedElement>
      </View>
      <Text style={{ color: 'white', fontSize: 25 }}>Some description</Text>
    </SafeAreaView>
  )
}

RecipeScreen.sharedElements = navigation => {
  const item = navigation.getParam('item')
  return [item.id]
}

export default RecipeScreen

my stackNavigator:

import { createSharedElementStackNavigator } from 'react-navigation-shared-element'
import MealPlanScreen from '../screens/mealPlanScreen'
import RecipeScreen from '../screens/recipeScreen'

export default createSharedElementStackNavigator(
  {
    MealPlan: MealPlanScreen,
    Recipe: RecipeScreen,
  },
  {
    headerMode: 'none',
    initialRouteName: 'MealPlan',
    defaultNavigationOptions: {
      cardStyleInterpolator: ({ current: { progress }}) => {
        return { cardStyle: { opacity: progress }}
      },
      cardStyle: {
        backgroundColor: 'transparent',
      },
    },
  }
)

versions:

    "react-native-shared-element": "^0.7.0",
    "react-native-safe-area-context": "^3.1.9",
    "react-navigation-shared-element": "^2.3.1",
    "react-navigation-stack": "^2.10.2",
    "react-navigation": "^4.0.0",

I hope anyone can help me :( im very stuck at this moment (haha)

SteveGreenley commented 3 years ago

I'm getting the problem too. This seems to be a regression. This was reported and fixed last year at the end of April but the problem has come back.

SteveGreenley commented 3 years ago

I've tracked the problem down to a gross bug in react-navigation-stack. The latest version that does not have the bug is 2.8.4. Execute the following command to fix the issue: npm i react-navigation-stack@2.8.4.

The bug is in StackView.tsx. A function named handleTransitionEnd should call onTransitionEnd but calls onTransitionStart instead. As a result react-navigation-shared-element never gets to replace the animated version of the shared element with the original one which would work correctly with the scrollview after the animation is complete.

SteveGreenley commented 3 years ago

I've posted a bug report to react-navigation - https://github.com/react-navigation/react-navigation/issues/9376

YisusMB commented 3 years ago

@SteveGreenley omg dude nice! i solved my problem! really appreciated :D

KawaljeetCuelogic commented 3 years ago

Hey @SteveGreenley . I am using this version "@react-navigation/stack": "^5.2.1" i.e., [v5]. And the issue I am facing is similar to what @YisusMB faced. But in my case, the transition on the previous screen goes out of the screen from the top left. Please have a look:

https://user-images.githubusercontent.com/79830566/109535679-d7ffdf80-7ae2-11eb-9c47-42db9060080a.mp4

My code:

Screen 1:


return (
       <TouchableOpacity
        onPress={() => {
          navigation.navigate(NavigationRoutes.Profile);
        }}>
        <SharedElement id="hey">
          <Text
            style={[
              styles.title_16,
              commonStyles.marginTop_60,
              commonStyles.marginBottom_10,
              commonStyles.alignSelfCenter,
            ]}>
            HEY THERE
          </Text>
        </SharedElement>
      </TouchableOpacity>
     )

Screen 2:

return (
<>
        <View
          style={[
            commonStyles.paddingBottom_20,
            commonStyles.positionRelative,
            commonStyles.dFlex,
            commonStyles.alignItemsCenter,
            Platform.OS === 'ios'
              ? commonStyles.marginTop_60
              : commonStyles.marginTop_20,
            styles.sectionBorder,
          ]}>
          <SharedElement id={`expertTitle`} style={styles.pageTitle}>
            <Text style={styles.pageTitle}>{strings.expert}</Text>
          </SharedElement>
          <TouchableOpacity
            style={[styles.actionIcon, commonStyles.marginLeft_20]}
            onPress={() => navigation.pop()}>
            <Image source={Icons.ic_chevron_left} />
          </TouchableOpacity>
        </View>
        <SharedElement id="hey">
          <Text
            style={[
              styles.title,
              commonStyles.marginTop_300,
              commonStyles.marginBottom_10,
              commonStyles.alignSelfCenter,
            ]}>
            HEY THERE
          </Text>
        </SharedElement>
      </>
    );

MDProfile.sharedElements = (route, otherRoute, showing) => {
  // const { request } = route.params;
  return [
    { id: 'hey', animation: 'fade' },
  ];
};

App.js

const sharedElementConfig = {
  headerShown: false,
  transitionSpec: {
    open: { animation: 'timing', config: { duration: 1000 } },
    close: { animation: 'timing', config: { duration: 1000 } },
  },
  cardStyleInterpolator: ({ current: { progress } }) => {
    return {
      cardStyle: {
        opacity: progress,
      },
    };
  },
};

<RootStack.Screen
          options={{ headerShown: false }}
          name={NavigationRoutes.Request}
          component={RequestDetails}
        />
        <RootStack.Screen
          options={{ headerShown: false }}
          name={NavigationRoutes.Profile}
          component={MDProfile}
          options={() => sharedElementConfig}
        />

Dependencies:

  "dependencies": {
    "react-native-animatable": "^1.3.3",
    "react-native-shared-element": "^0.7.0",
    "react-native-splash-screen": "^3.2.0",
    "react-navigation": "^4.4.3",
    "react-navigation-shared-element": "^5.0.0-alpha1",
    "@react-native-community/async-storage": "^1.12.1",
    "@react-native-community/masked-view": "^0.1.10",
    "@react-navigation/bottom-tabs": "^5.11.2",
    "@react-navigation/native": "^5.1.0",
    "@react-navigation/stack": "^5.2.1",
    "metro-react-native-babel-preset": "^0.65.0",
    "react": "16.13.1",
    "react-native": "0.63.4",
    "react-native-gesture-handler": "^1.9.0",
    "react-native-linear-gradient": "^2.5.6",
    "react-native-safe-area-context": "^3.1.9",
    "react-native-screens": "^2.16.1",
    "react-native-vector-icons": "^7.1.0",
  }

PS: Issue persists only on the iOS platform, Android works well with the same code and setup.

Any kind of help would be much appreciated.

chiefchief commented 3 years ago

@KawaljeetCuelogic answer please when you find solution Same behavior with example app

https://user-images.githubusercontent.com/32684425/109558276-de418c00-7ae1-11eb-9564-b2de101f21c8.mov

First Screen:

export default class MainScreen extends React.Component {
  onPress = () => {
    this.props.navigation.push('DetailScreen');
  };
  render() {
    return (
      <View style={{flex: 1}}>
        <TouchableOpacity style={{width: 200, height: 200}} onPress={this.onPress}>
          <View style={styles.container}>
            <SharedElement style={styles.image} id="image">
              <Image style={styles.image} source={require('./theboys.jpg')} />
            </SharedElement>
            <SharedElement id="text">
              <Text style={styles.text}>The Boys</Text>
            </SharedElement>
            <Text style={styles.caption}>tap me</Text>
          </View>
        </TouchableOpacity>
      </View>
    );
  }
}

Second Screen:

const DetailScreen: React.FC = () => (
  <View style={{flex: 1}}>
    <View style={styles.container}>
      <SharedElement id="image" style={{width, height: width}}>
        <Image style={styles.image} source={require('./theboys.jpg')} />
      </SharedElement>
      <SharedElement id="text">
        <Text style={styles.text}>The Boys</Text>
      </SharedElement>
    </View>
  </View>
);
DetailScreen.sharedElements = (route, otherRoute, showing) => {
  return [{id: 'image'}, {id: 'text', animation: 'fade'}];
};

export default DetailScreen;

Navigation:

    <SharedStack.Navigator>
      <SharedStack.Screen name={'SharedElement'} component={SharedElement} />
      <SharedStack.Screen
        name={'DetailScreen'}
        component={DetailScreen}
        options={() => ({
          gestureEnabled: false,
          transitionSpec: {
            open: {animation: 'timing', config: {duration: 1000}},
            close: {animation: 'timing', config: {duration: 1000}},
          },
          cardStyleInterpolator: ({current: {progress}}) => {
            return {
              cardStyle: {
                opacity: progress,
              },
            };
          },
        })}
      />
    </SharedStack.Navigator>

Dependencies: react-native-cli

  "dependencies": {
    "@react-native-community/masked-view": "^0.1.10",
    "@react-navigation/bottom-tabs": "^5.11.7",
    "@react-navigation/native": "^5.9.2",
    "@react-navigation/stack": "^5.14.2",
    "react": "16.13.1",
    "react-native": "0.63.3",
    "react-native-safe-area-context": "^3.1.9",
    "react-native-screens": "^2.17.1",
    "react-native-shared-element": "^0.7.0",
    "react-navigation-shared-element": "^5.0.0-alpha1",
  },
KawaljeetCuelogic commented 3 years ago

@chiefchief Haven't found any solution for it mate. Seems like this library is not well maintained :(

chiefchief commented 3 years ago

@KawaljeetCuelogic i've found solution in closed issues - https://github.com/IjzerenHein/react-navigation-shared-element/issues/130 remove enableScreens();

KawaljeetSBagga commented 3 years ago

Hey @chiefchief Thanks alot for your efforts man. That worked.

Planetfunky commented 3 years ago

I'm having the same issue on an expo project (sdk 41). It certainly looks fine on Android but on Ios the shared element goes to the top left corner when going back. Hope this issue get solved. Btw enableScreens() doesnt really help.