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

Shared element stuck in list when navigateíng back #24

Closed
kledk opened this issue Feb 20, 2020 · 14 comments
Closed

Shared element stuck in list when navigateíng back #24

kledk opened this issue Feb 20, 2020 · 14 comments

Comments

@kledk
Copy link

kledk commented Feb 20, 2020

Anybody ever had an issue like this?
My shared element is somehow getting stuck when I navigate back to the list.

Kapture 2020-02-20 at 13 24 16

@IjzerenHein
Copy link
Owner

This means that the shared-element transition is still in progress. While it is in progress, it keeps the original item hidden and draws the overlay.
I think this is because the onTransitionEnd event from the stack lib is received too late. It's possible that the transition-spec is using a spring and it takes a while for it to settle. Are you also seeing this on Android?

@kledk
Copy link
Author

kledk commented Feb 21, 2020

Okay. Yes I also see it on Android.

@IjzerenHein
Copy link
Owner

Alright. How does the rest of your navigation/stack look like?

@davidrenji
Copy link

I'm facing a similar issue. Is there a way to know when the transitions back has finished in the origin screen?

@MoeMamdouh
Copy link

Any updates in this issue?

@IjzerenHein
Copy link
Owner

Here's some log output that illustrates the problem. From the time the default transition starts and up until it ends, it takes almost 1 second, which is too much.

09:40:08.912 SharedElementRendererData.js:1 startTransition[StackNavigator1], closing: true, nestingDepth: 2
09:40:08.913 SharedElementRendererData.js:1 willFocusScene[StackNavigator1], name: "MainScreen", depth: 2
09:40:08.913 SharedElementRendererData.js:1 willBlurScene[StackNavigator1], name: "DetailScreen", depth: 2
09:40:08.914 SharedElementRendererData.js:1 willBlurScene[StackNavigator1] using Animated.Value from "DetailScreen", animValue: [object Object]
09:40:08.914 SharedElementRendererData.js:1 Transitioning from "DetailScreen" to "MainScreen", elements: [{"id":"image","otherId":"image","animation":"move"},{"id":"text","otherId":"text","animation":"fade"}]
09:40:09.838 SharedElementRendererData.js:1 endTransition[StackNavigator1], closing: true, nestingDepth: 2
09:40:09.838 SharedElementRendererData.js:1 Transitioning from "undefined" to "MainScreen", elements: null
09:40:09.853 SharedElementRendererData.js:1 didFocusScene[StackNavigator1], name: "MainScreen", depth: 2

The default react-navigation-stack transitionSpec config on iOS looks like this:

export const TransitionIOSSpec: TransitionSpec = {
  animation: 'spring',
  config: {
    stiffness: 1000,
    damping: 500,
    mass: 3,
    overshootClamping: true,
    restDisplacementThreshold: 0.01,
    restSpeedThreshold: 0.01,
  },
};

It seems that the values are optimized for smoothness, but this also causes the spring-particle to "settle" very late, causing the problem that you witnessed.

For instance, when I change the transitionSpec to a timing based function the problem is gone:

const StackNavigator1 = createSharedElementStackNavigator(
  {
    Main: MainScreen,
    Detail: DetailScreen,
  },
  {
    defaultNavigationOptions: {
      transitionSpec: {
        open: {
          animation: 'timing',
          config: {
            duration: 350,
          },
        },
        close: {
          animation: 'timing',
          config: {
            duration: 350,
          },
        },
      },
    },
  },
  {
    name: 'StackNavigator1',
  },
);
09:51:02.836 SharedElementRendererData.js:1 startTransition[StackNavigator1], closing: false, nestingDepth: 2
09:51:02.837 SharedElementRendererData.js:1 willBlurScene[StackNavigator1], name: "MainScreen", depth: 2
09:51:02.838 SharedElementRendererData.js:1 willFocusScene[StackNavigator1], name: "DetailScreen", depth: 2
09:51:02.838 SharedElementRendererData.js:1 willFocusScene[StackNavigator1] using Animated.Value from "DetailScreen", animValue: [object Object]
09:51:02.838 SharedElementRendererData.js:1 Transitioning from "MainScreen" to "DetailScreen", elements: [{"id":"image","otherId":"image","animation":"move"},{"id":"text","otherId":"text","animation":"fade"}]
09:51:03.038 RCTLog.js:47 [PERF ASSETS] Loading image at size {2296, 1450}, which is larger than the screen size {828, 1792}
09:51:03.218 SharedElementRendererData.js:1 endTransition[StackNavigator1], closing: false, nestingDepth: 2
09:51:03.218 SharedElementRendererData.js:1 Transitioning from "undefined" to "DetailScreen", elements: null
09:51:03.236 SharedElementRendererData.js:1 didFocusScene[StackNavigator1], name: "DetailScreen", depth: 2

@IjzerenHein
Copy link
Owner

export const TransitionIOSSpec: TransitionSpec = {
  animation: 'spring',
  config: {
    stiffness: 1000,
    damping: 500,
    mass: 3,
    overshootClamping: true,
    restDisplacementThreshold: 0.01,
    restSpeedThreshold: 0.01,
  },
};

If would be nice if someone could "fine-tune" the iOS spring config so that it settles way faster and perhaps even push a PR to react-navigation-stack for this?

@IjzerenHein IjzerenHein added the help wanted Extra attention is needed label Feb 26, 2020
@IjzerenHein
Copy link
Owner

I've done some testing and found the following settings quite effective in making the spring on iOS come to a rest much faster, while not compromising smoothness. The spring settles in about 500ms, instead of 1000ms.

export const iosTransitionSpec = {
  animation: 'spring',
  config: {
    stiffness: 1000,
    damping: 500,
    mass: 3,
    overshootClamping: true,
    restDisplacementThreshold: 10,
    restSpeedThreshold: 10,
  },
};

// Example usage
const StackNavigator1 = createSharedElementStackNavigator(
  {
    Main: MainScreen,
    Detail: DetailScreen,
  },
  {
    defaultNavigationOptions: {
      onTransitionStart: () => console.log('onTransitionStart'),
      onTransitionEnd: () => console.log('onTransitionEnd'),
      transitionSpec: {
        open: iosTransitionSpec,
        close: iosTransitionSpec,
      },
    },
  },
  {
    name: 'StackNavigator1',
  },
);

Let me know whether this solves your problem @kledk ?

@IjzerenHein IjzerenHein removed the help wanted Extra attention is needed label Mar 15, 2020
@kledk
Copy link
Author

kledk commented Mar 16, 2020

@IjzerenHein Cool, I will try that out as soon as possible!

@sergchernata
Copy link

sergchernata commented Mar 26, 2020

Transition spec posted above seems to solve all of my issues.

The one remaining problems is opacity. An element wrapped in TouchableOpacity stays faded until the animation has completely settled.

@IjzerenHein
Copy link
Owner

Transition spec posted above seems to solve all of my issues.

The one remaining problems is opacity. An element wrapped in TouchableOpacity stays faded until the animation has completely settled.

Hi, please create a separate issue for this and post a (slow-motion) video of the problem.

@IjzerenHein
Copy link
Owner

I've issued a PR to update the transitionSpec in react-navigation rest times to more meaningful values:
react-navigation/react-navigation#8028

satya164 pushed a commit to react-navigation/react-navigation that referenced this issue May 11, 2020
# Why

When using the stack navigator on iOS, it takes a (too) long time before the  `didFocus` and stack `onTransitionEnd` lifecycle events are triggered. The visual animation is typically completed well within 500 msec, but it takes around 1000 msec before the previously mentioned events are emitted. This causes problems with for instance `react-navigation-shared-element`, which relies on these events to fire in a timely manner(IjzerenHein/react-navigation-shared-element#24)

# How

This PR updates the resting threshold so that the underlying spring settles faster. No visual differences or differences in smoothness were witnessed during testing.

## Before

Time to settle `didFocus`: **941**
Time to settle `stack.onTransitionEnd`: **924**

```
15:59:55.743 [ListViewStack]startTransition, closing: false, nestingDepth: 1
15:59:55.744 [ListViewStack]willFocus, scene: "DetailScreen", depth: 1, closing: false
15:59:55.745 Transition start: "ListScreen" -> "DetailScreen"
15:59:56.667 [ListViewStack]endTransition, closing: false, nestingDepth: 1
15:59:56.668 Transition end: "DetailScreen"
15:59:56.685 [ListViewStack]didFocus, scene: "DetailScreen", depth: 1
```

## After

Time to settle `didFocus`: **529**
Time to settle `stack.onTransitionEnd`: **512**

```
15:55:00.686 [ListViewStack]startTransition, closing: false, nestingDepth: 1
15:55:00.687 [ListViewStack]willFocus, scene: "DetailScreen", depth: 1, closing: false
15:55:00.687 Transition start: "ListScreen" -> "DetailScreen"
15:55:01.198 [ListViewStack]endTransition, closing: false, nestingDepth: 1
15:55:01.199 Transition end: "DetailScreen"
15:55:01.216 [ListViewStack]didFocus, scene: "DetailScreen", depth: 1
@wzahedi
Copy link

wzahedi commented May 21, 2020

I've done some testing and found the following settings quite effective in making the spring on iOS come to a rest much faster, while not compromising smoothness. The spring settles in about 500ms, instead of 1000ms.

export const iosTransitionSpec = {
  animation: 'spring',
  config: {
    stiffness: 1000,
    damping: 500,
    mass: 3,
    overshootClamping: true,
    restDisplacementThreshold: 10,
    restSpeedThreshold: 10,
  },
};

// Example usage
const StackNavigator1 = createSharedElementStackNavigator(
  {
    Main: MainScreen,
    Detail: DetailScreen,
  },
  {
    defaultNavigationOptions: {
      onTransitionStart: () => console.log('onTransitionStart'),
      onTransitionEnd: () => console.log('onTransitionEnd'),
      transitionSpec: {
        open: iosTransitionSpec,
        close: iosTransitionSpec,
      },
    },
  },
  {
    name: 'StackNavigator1',
  },
);

Let me know whether this solves your problem @kledk ?

I tried this on iOS, however it still does not work. I've attached some code, and a video example below. Any advice appreciated! Seems to also be related to this: #72
https://streamable.com/tyslsd

//Transition:
const Transition = {
  gestureDirection: 'vertical',
  transitionSpec: {
    open: iosTransitionSpec,
    close: iosTransitionSpec,
  },
  cardStyleInterpolator: ({ current }) => {
    return {
      cardStyle: {
        opacity: current.progress,
      },
      overlayStyle: {
        opacity: current.progress.interpolate({
          inputRange: [0, 1],
          outputRange: [0, 0.5],
        }),
      },
    };
  },
};

//Screen:
<Stack.Screen
   name="PhotoView"
   component={PhotoView}
   options={{
      ...Transition,
   }}
   sharedElementsConfig={(route) => {
      const { uri } = route.params;
      return [
         { id: uri, animation: 'fade', align: 'center-center' },
      ];
    }}
 />

@dochonglo
Copy link

dochonglo commented Jul 22, 2021

I tried the same exact solution above regarding altering the transitionSpec on iOS. However, I'm still running into the same problem with the SharedElement staying stuck in place while scrolling through a FlatList. I've even tried altering the values of the durations unfortunately, there's still no difference.

const DiscoverTabStack = createSharedElementStackNavigator( createStackNavigator, { Discover: { screen: DiscoverScreen, path: "discover" }, DetailedActivity: DetailedActivityScreen, }, { animationEnabled: true, gestureVelocityImpact: 0.5, cardOverlayEnabled: true, gestureResponseDistance: { vertical: 100, }, defaultNavigationOptions: { transitionSpec: { open: { animation: "timing", config: { duration: 100, }, }, close: { animation: "timing", config: { duration: 50, }, }, }, }, } );

Currently, I'm using the following versions:

  • "react-native-shared-element": "^0.7.0"
  • "react-navigation-shared-element": "1"
  • "react-navigation-stack": "^1.10.3"
  • "react-navigation-tabs": "^2.6.2"
  • "react-navigation": "^4.0.10"
  • "react-native-screens": "~2.15.0"

Anyone know what the problem may be?

joshuapinter pushed a commit to cntral/react-navigation that referenced this issue Sep 29, 2021
# Why

When using the stack navigator on iOS, it takes a (too) long time before the  `didFocus` and stack `onTransitionEnd` lifecycle events are triggered. The visual animation is typically completed well within 500 msec, but it takes around 1000 msec before the previously mentioned events are emitted. This causes problems with for instance `react-navigation-shared-element`, which relies on these events to fire in a timely manner(IjzerenHein/react-navigation-shared-element#24)

# How

This PR updates the resting threshold so that the underlying spring settles faster. No visual differences or differences in smoothness were witnessed during testing.

## Before

Time to settle `didFocus`: **941**
Time to settle `stack.onTransitionEnd`: **924**

```
15:59:55.743 [ListViewStack]startTransition, closing: false, nestingDepth: 1
15:59:55.744 [ListViewStack]willFocus, scene: "DetailScreen", depth: 1, closing: false
15:59:55.745 Transition start: "ListScreen" -> "DetailScreen"
15:59:56.667 [ListViewStack]endTransition, closing: false, nestingDepth: 1
15:59:56.668 Transition end: "DetailScreen"
15:59:56.685 [ListViewStack]didFocus, scene: "DetailScreen", depth: 1
```

## After

Time to settle `didFocus`: **529**
Time to settle `stack.onTransitionEnd`: **512**

```
15:55:00.686 [ListViewStack]startTransition, closing: false, nestingDepth: 1
15:55:00.687 [ListViewStack]willFocus, scene: "DetailScreen", depth: 1, closing: false
15:55:00.687 Transition start: "ListScreen" -> "DetailScreen"
15:55:01.198 [ListViewStack]endTransition, closing: false, nestingDepth: 1
15:55:01.199 Transition end: "DetailScreen"
15:55:01.216 [ListViewStack]didFocus, scene: "DetailScreen", depth: 1
YAfullStack pushed a commit to YAfullStack/React-navigation that referenced this issue Dec 4, 2021
# Why

When using the stack navigator on iOS, it takes a (too) long time before the  `didFocus` and stack `onTransitionEnd` lifecycle events are triggered. The visual animation is typically completed well within 500 msec, but it takes around 1000 msec before the previously mentioned events are emitted. This causes problems with for instance `react-navigation-shared-element`, which relies on these events to fire in a timely manner(IjzerenHein/react-navigation-shared-element#24)

# How

This PR updates the resting threshold so that the underlying spring settles faster. No visual differences or differences in smoothness were witnessed during testing.

## Before

Time to settle `didFocus`: **941**
Time to settle `stack.onTransitionEnd`: **924**

```
15:59:55.743 [ListViewStack]startTransition, closing: false, nestingDepth: 1
15:59:55.744 [ListViewStack]willFocus, scene: "DetailScreen", depth: 1, closing: false
15:59:55.745 Transition start: "ListScreen" -> "DetailScreen"
15:59:56.667 [ListViewStack]endTransition, closing: false, nestingDepth: 1
15:59:56.668 Transition end: "DetailScreen"
15:59:56.685 [ListViewStack]didFocus, scene: "DetailScreen", depth: 1
```

## After

Time to settle `didFocus`: **529**
Time to settle `stack.onTransitionEnd`: **512**

```
15:55:00.686 [ListViewStack]startTransition, closing: false, nestingDepth: 1
15:55:00.687 [ListViewStack]willFocus, scene: "DetailScreen", depth: 1, closing: false
15:55:00.687 Transition start: "ListScreen" -> "DetailScreen"
15:55:01.198 [ListViewStack]endTransition, closing: false, nestingDepth: 1
15:55:01.199 Transition end: "DetailScreen"
15:55:01.216 [ListViewStack]didFocus, scene: "DetailScreen", depth: 1
redhawkIT pushed a commit to redhawkIT/react-navigation that referenced this issue Dec 23, 2021
# Why

When using the stack navigator on iOS, it takes a (too) long time before the  `didFocus` and stack `onTransitionEnd` lifecycle events are triggered. The visual animation is typically completed well within 500 msec, but it takes around 1000 msec before the previously mentioned events are emitted. This causes problems with for instance `react-navigation-shared-element`, which relies on these events to fire in a timely manner(IjzerenHein/react-navigation-shared-element#24)

# How

This PR updates the resting threshold so that the underlying spring settles faster. No visual differences or differences in smoothness were witnessed during testing.

## Before

Time to settle `didFocus`: **941**
Time to settle `stack.onTransitionEnd`: **924**

```
15:59:55.743 [ListViewStack]startTransition, closing: false, nestingDepth: 1
15:59:55.744 [ListViewStack]willFocus, scene: "DetailScreen", depth: 1, closing: false
15:59:55.745 Transition start: "ListScreen" -> "DetailScreen"
15:59:56.667 [ListViewStack]endTransition, closing: false, nestingDepth: 1
15:59:56.668 Transition end: "DetailScreen"
15:59:56.685 [ListViewStack]didFocus, scene: "DetailScreen", depth: 1
```

## After

Time to settle `didFocus`: **529**
Time to settle `stack.onTransitionEnd`: **512**

```
15:55:00.686 [ListViewStack]startTransition, closing: false, nestingDepth: 1
15:55:00.687 [ListViewStack]willFocus, scene: "DetailScreen", depth: 1, closing: false
15:55:00.687 Transition start: "ListScreen" -> "DetailScreen"
15:55:01.198 [ListViewStack]endTransition, closing: false, nestingDepth: 1
15:55:01.199 Transition end: "DetailScreen"
15:55:01.216 [ListViewStack]didFocus, scene: "DetailScreen", depth: 1
darkhorse-coder pushed a commit to darkhorse-coder/react-navigation that referenced this issue Apr 7, 2022
# Why

When using the stack navigator on iOS, it takes a (too) long time before the  `didFocus` and stack `onTransitionEnd` lifecycle events are triggered. The visual animation is typically completed well within 500 msec, but it takes around 1000 msec before the previously mentioned events are emitted. This causes problems with for instance `react-navigation-shared-element`, which relies on these events to fire in a timely manner(IjzerenHein/react-navigation-shared-element#24)

# How

This PR updates the resting threshold so that the underlying spring settles faster. No visual differences or differences in smoothness were witnessed during testing.

## Before

Time to settle `didFocus`: **941**
Time to settle `stack.onTransitionEnd`: **924**

```
15:59:55.743 [ListViewStack]startTransition, closing: false, nestingDepth: 1
15:59:55.744 [ListViewStack]willFocus, scene: "DetailScreen", depth: 1, closing: false
15:59:55.745 Transition start: "ListScreen" -> "DetailScreen"
15:59:56.667 [ListViewStack]endTransition, closing: false, nestingDepth: 1
15:59:56.668 Transition end: "DetailScreen"
15:59:56.685 [ListViewStack]didFocus, scene: "DetailScreen", depth: 1
```

## After

Time to settle `didFocus`: **529**
Time to settle `stack.onTransitionEnd`: **512**

```
15:55:00.686 [ListViewStack]startTransition, closing: false, nestingDepth: 1
15:55:00.687 [ListViewStack]willFocus, scene: "DetailScreen", depth: 1, closing: false
15:55:00.687 Transition start: "ListScreen" -> "DetailScreen"
15:55:01.198 [ListViewStack]endTransition, closing: false, nestingDepth: 1
15:55:01.199 Transition end: "DetailScreen"
15:55:01.216 [ListViewStack]didFocus, scene: "DetailScreen", depth: 1
nenad0212 pushed a commit to nenad0212/temp1 that referenced this issue Sep 6, 2022
# Why

When using the stack navigator on iOS, it takes a (too) long time before the  `didFocus` and stack `onTransitionEnd` lifecycle events are triggered. The visual animation is typically completed well within 500 msec, but it takes around 1000 msec before the previously mentioned events are emitted. This causes problems with for instance `react-navigation-shared-element`, which relies on these events to fire in a timely manner(IjzerenHein/react-navigation-shared-element#24)

# How

This PR updates the resting threshold so that the underlying spring settles faster. No visual differences or differences in smoothness were witnessed during testing.

## Before

Time to settle `didFocus`: **941**
Time to settle `stack.onTransitionEnd`: **924**

```
15:59:55.743 [ListViewStack]startTransition, closing: false, nestingDepth: 1
15:59:55.744 [ListViewStack]willFocus, scene: "DetailScreen", depth: 1, closing: false
15:59:55.745 Transition start: "ListScreen" -> "DetailScreen"
15:59:56.667 [ListViewStack]endTransition, closing: false, nestingDepth: 1
15:59:56.668 Transition end: "DetailScreen"
15:59:56.685 [ListViewStack]didFocus, scene: "DetailScreen", depth: 1
```

## After

Time to settle `didFocus`: **529**
Time to settle `stack.onTransitionEnd`: **512**

```
15:55:00.686 [ListViewStack]startTransition, closing: false, nestingDepth: 1
15:55:00.687 [ListViewStack]willFocus, scene: "DetailScreen", depth: 1, closing: false
15:55:00.687 Transition start: "ListScreen" -> "DetailScreen"
15:55:01.198 [ListViewStack]endTransition, closing: false, nestingDepth: 1
15:55:01.199 Transition end: "DetailScreen"
15:55:01.216 [ListViewStack]didFocus, scene: "DetailScreen", depth: 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants