software-mansion / software-mansion/react-native-reanimated

Blank element and wrong position after shared transition end

Open
#4,237 4 comments 5 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Area: Shared Element Transitions Missing repro Needs review Platform: iOS
Dominant language
TypeScript
Stars
11k
Forks
1.5k
Avg merge
3d 4h
Merged PRs (30d)
172

Description

Description

I create a project to test shared animation.
when i click new transition when old transition not end yet.
old element is blank( i think opacity is 0 )
Then, transition working wrong.
See my video:

https://user-images.githubusercontent.com/43195241/225859358-9903a559-dd62-4b8c-99a3-27d1b62c956e.mp4

My code:

import {NavigationContainer, useNavigation} from '@react-navigation/native';
import {createNativeStackNavigator} from '@react-navigation/native-stack';
import React from 'react';
import {
  Dimensions,
  FlatList,
  ListRenderItemInfo,
  SafeAreaView,
  StyleSheet,
  TouchableOpacity,
} from 'react-native';
import {GestureHandlerRootView} from 'react-native-gesture-handler';
import Animated, {
  Easing,
  SharedTransition,
  withSequence,
  withTiming,
} from 'react-native-reanimated';

const {width} = Dimensions.get('window');

const SIZE_IMAGE = width / 3;

export type ImageType = {
  id: string;
};

const dataImages = [
  'https://fastly.picsum.photos/id/756/300/300.jpg?hmac=ihyfC35Qzh798wqV3x_k5A3jEaY9lRiC4w1azntXksM',
  'https://fastly.picsum.photos/id/71/300/300.jpg?hmac=lavbXU_aeHIi1agvKBKJYbgbtDFTCCdGDuzngtJVQjE',
  'https://fastly.picsum.photos/id/1012/300/300.jpg?hmac=FpdcKpoTxl0CZdLj7RjrTjISpiu8TtsesR9f4Fm2yOk',
  'https://fastly.picsum.photos/id/794/300/300.jpg?hmac=EOGWbvHbcjble_4y30-aBz6-AcjjWyNAfFlbfmS1r9U',
  'https://fastly.picsum.photos/id/634/300/300.jpg?hmac=Xydl14x40_5ZjRDqaIAqxyQcuSub_xDcabmUtuE-eD8',
  'https://fastly.picsum.photos/id/862/300/300.jpg?hmac=omllSASwYQXWPO584bLQUz6GetRL0GRe-2QctRXVnJA',
  'https://fastly.picsum.photos/id/3/300/300.jpg?hmac=RT2JK6MzdIgNIWoIj61uPcz8aOSOi3lu2vhnwOxs7lY',
  'https://fastly.picsum.photos/id/639/300/300.jpg?hmac=dQz9oW6E7gTcgpjQvsXItRygy6Z5kL2mc5sqN8bTeRw',
  'https://fastly.picsum.photos/id/840/300/300.jpg?hmac=jYWhgnilJlqSvqrNyakWLPa67a5HJcAVGKo5vbXfJWc',
  'https://fastly.picsum.photos/id/994/300/300.jpg?hmac=GDm1Mpq1ylmjwx5Dv9u-l1nNEAKFDBQyanBzp7Nl4yg',
  'https://fastly.picsum.photos/id/242/300/300.jpg?hmac=o4XyRlR53cSyXOhNnSoYw3pNc6AiUawAVed124gdQZs',
  'https://fastly.picsum.photos/id/857/300/300.jpg?hmac=_2o0QaHplHuWOr4ETeEP5_3Wo6lgNhoxZgeb0yjFckc',
  'https://fastly.picsum.photos/id/217/300/300.jpg?hmac=BsPwAcRl68EZm3jQkeJOcTySx7bzPELlYkwkkl-itpc',
  'https://fastly.picsum.photos/id/1041/300/300.jpg?hmac=PosL0KGsFiR_uaOxVY5r9pvWRIyIpn07BSV53jT6We0',
  'https://fastly.picsum.photos/id/858/300/300.jpg?hmac=_ejZ2a4fSroS4BO_gXXIxq7hyd0RIHSP290jgbqMO2c',
];

export type ImageExpoProps = {
  image: ImageType;
};

const transition = SharedTransition.custom(values => {
  'worklet';

  return {
    height: withTiming(values.targetHeight, {duration: 300}),
    width: withTiming(values.targetWidth, {duration: 300}),
    originX: withTiming(values.targetOriginX, {duration: 300}),
    originY: withSequence(
      withTiming(values.currentOriginY + 50, {
        duration: 300,
        easing: Easing.linear,
      }),
      withTiming(values.targetOriginY, {
        duration: 500,
        easing: Easing.linear,
      }),
    ),
  };
});

const ImageExpo = ({url}) => {
  const navigation = useNavigation();
  return (
    <TouchableOpacity
      onPress={() => {
        navigation.navigate('Detail', {url});
      }}>
      <Animated.View
        sharedTransitionStyle={transition}
        sharedTransitionTag={`container_${url}`}
        style={styles.image}>
        <Animated.Image
          sharedTransitionStyle={transition}
          sharedTransitionTag={`image_${url}`}
          resizeMode="cover"
          source={{uri: url}}
          style={StyleSheet.absoluteFillObject}
        />
      </Animated.View>
    </TouchableOpacity>
  );
};

const ListImage = () => {
  // func
  const renderImage = ({item}: ListRenderItemInfo<string>) => {
    return <ImageExpo url={item} />;
  };

  const keyExtractor = (item: string) => {
    return item;
  };

  // render
  return (
    <SafeAreaView style={styles.root}>
      <FlatList
        numColumns={3}
        renderItem={renderImage}
        keyExtractor={keyExtractor}
        data={dataImages}
      />
    </SafeAreaView>
  );
};

const DetailImage = ({
  route: {
    params: {url},
  },
}) => {
  return (
    <>
      <SafeAreaView style={styles.root}>
        <Animated.View
          sharedTransitionStyle={transition}
          sharedTransitionTag={`container_${url}`}
          style={styles.detailImage}>
          <Animated.Image
            sharedTransitionStyle={transition}
            sharedTransitionTag={`image_${url}`}
            resizeMode="cover"
            source={{uri: url}}
            style={StyleSheet.absoluteFillObject}
          />
        </Animated.View>
      </SafeAreaView>
    </>
  );
};

const Stack = createNativeStackNavigator();

const App = () => {
  return (
    <GestureHandlerRootView style={styles.root}>
      <NavigationContainer>
        <Stack.Navigator>
          <Stack.Screen name="List" component={ListImage} />
          <Stack.Screen
            options={{
              animation: 'slide_from_bottom',
            }}
            name="Detail"
            component={DetailImage}
          />
        </Stack.Navigator>
      </NavigationContainer>
    </GestureHandlerRootView>
  );
};

const styles = StyleSheet.create({
  root: {
    flex: 1,
  },
  detailImage: {
    width: width,
    height: 200,
    overflow: 'hidden',
    justifyContent: 'center',
    alignItems: 'center',
  },
  image: {
    justifyContent: 'center',
    alignItems: 'center',
    width: SIZE_IMAGE,
    height: SIZE_IMAGE,
    overflow: 'hidden',
    borderWidth: 1,
    borderColor: '#ffffff',
    borderRadius: 10,
  },
});

export default App;

Steps to reproduce
  1. Create list image
  2. Navigate to detail screen
  3. Click back button
  4. Before animated end, click new screen (Detail screen)
Snack or a link to a repository

I sent my code above

Reanimated version

3.0.2

React Native version

0.71.4

Platforms

iOS

JavaScript runtime

None

Workflow

React Native (without Expo)

Architecture

Paper (Old Architecture)

Build type

Debug mode

Device

iOS simulator

Device model

No response

Acknowledgements

Yes

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the issue with the provided TypeScript example on iOS, using React Native 0.71.4 and Reanimated 3.0.2. Exercise the back-navigation path and trigger another detail transition before the first animation ends; done means the shared element is neither blank nor positioned incorrectly after the interrupted transition.

Written by the indexing model from the issue text.

Assessment

Tech stack
react-native, typescript
Domain
mobile-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.