react-native-screens icon indicating copy to clipboard operation
react-native-screens copied to clipboard

🦸🏻‍♂️ Hero/Shared Element Animation for Native Stack Navigator

Open mrousavy opened this issue 4 years ago • 18 comments

Intro

Hi!

I'm currently using react-navigation-shared-element to create shared element transitions between screens, which uses the StackNavigator from react-navigation under the hood (so not the native one from react-native-screens, there's an issue for this here) and therefore suffers from bad performance. (It is an awesome library though!)

Approach

So I'm trying to come up with performant alternatives. I have thought about creating a Native Module/library which wraps the Hero library and an Android alternative, so you can declare React Components like this:

<Hero id="image1">
  <Image source={source} ... />
</Hero>

... wrap those in a Native Stack Navigator and let the library automatically create Shared Element / Hero Transitions for you on each navigation (props.navigation.navigate(...)).

      

So why am I opening this issue?

I wanted to ask first before I try to dig around the source code and eventually waste a lot of time:

  1. How is the Native Stack Navigator implemented, does it essentially create multiple View Controllers between routes?
  2. If it does, do I have access to those View Controllers? (That wouldn't be the biggest problem, since I can use parentResponder in a View to go up the View tree anyway)
  3. Do you have any advice I should look out for?

If it does not create multiple View Controllers, then I guess there would be no way of getting the Hero library to work, since that requires navigation between multiple View Controllers.

Thanks!

mrousavy avatar Jul 10 '20 11:07 mrousavy

hey @mrousavy – Thanks for putting some thought into that. We'd be open to collaborating on that together. In fact we already had some plans to work in this in the near future.

As for the idea of using hero – as much as I like the library and its API I'd imagine it being difficult to integrate. The biggest issue here IMO is that it hides a lot of complexity exposing a set of available transitions. It makes a lot of sense when you use it in swift as it is very easy to use the built-in options. However, if we wanted to use it from React Native we'd have to expose the available options. This may not be too difficult, but then when we'd want to adapt it to Android we'd need to replicate each of those effects to make it work consistently across different platforms. The preferred approach would be to expose a way to control transition using the existing animation libraries (e.g. Animated or Reanimated). This way the API surface would be much smaller and we'd defer the implementation of certain transition to APIs that already work across all supported platforms.

Answering your questions:

  1. Native Stack renders a UINavigationViewController and each of the screen that is rendered on the stack is encapsulated in a UIVIewController. The VC structure is exactly as the one that would've been created in a objc or a swift app.
  2. Yes, there are two "container types". One is called "ScreenCintainer" and the second is "ScreenStack". Container just uses UIViewController directly whereas Stack uses UINavController. From the native side we operate on those controllers and can access all the children controllers when necessary. Each "Screen" is a UIViewController with a UIView that render the content. They are interlinked together so you can access controller from the view and vice versa.
  3. There is a lot of good stuff here done by @IjzerenHein here https://github.com/IjzerenHein/react-native-shared-element – definitely worth checking out. The only aspect is that RNSE lib does not rely on the concept of UIViewControllers. Not sure how it'd behave if views are shared across different VC's.

kmagiera avatar Jul 21 '20 11:07 kmagiera

@kmagiera Thanks for your reply. After I initially posted my question, I've begun to dig around the source code and tried some things in a sandbox app. I managed to get a really wacky solution working with a "fire and forget" animation concept, but to get this working fully I will need to do a lot of polishing. Also, not sure if I want to have absolutely no control over the animation after I start it. I'm thinking we can't get around exposing an animated value which you can then use to interpolate on. see #317

mrousavy avatar Jul 21 '20 12:07 mrousavy

There is a lot of good stuff here done by @IjzerenHein here https://github.com/IjzerenHein/react-native-shared-element – definitely worth checking out. The only aspect is that RNSE lib does not rely on the concept of UIViewControllers. Not sure how it'd behave if views are shared across different VC's.

There are really two important parts here. Firstly the measuring part, where RNSE needs to figure out the start and ending positions of the elements. When using ViewControllers, you'd need to be able to calculate the ending position relative to the new view-controller. And secondly, the rendering. RNSE relies on rendering the visual content in front of the other views. So al long as both those things can be achieved, it should be possible.

IjzerenHein avatar Jul 22 '20 08:07 IjzerenHein

@IjzerenHein The react-native-shared-element renders infront of a single ViewController as of right now, right?

mrousavy avatar Jul 22 '20 13:07 mrousavy

@mrousavy That's up to you. It renders in whatever <View> you provide it.

IjzerenHein avatar Jul 22 '20 13:07 IjzerenHein

So would you say react-native-shared-element is compatible with this lib? @IjzerenHein

zachgibson avatar Oct 21 '20 22:10 zachgibson

Just checking, has anyone managed to get shared element transitions to work with native-stack, either with RNSE or a different solution?

Thanks!

nandorojo avatar Dec 01 '20 22:12 nandorojo

@nandorojo I've tried a different approach. I switched to the wix/react-native-navigation library and tried the shared element transitions. They had a lot of issues at first, but at least a foundation was implemented. I decided to create some issues, which later turned into me creating PRs and now I'm a maintainer over there. With the incredible help of the other maintainers, we managed to get shared element transitions fully working, in just about 2-3 months. Fully working means:

  • Fully native, 60 FPS
  • Custom interpolation/easing implementations (E.g.: Spring)
  • Border Radius Support
  • Image and FastImage support
  • Text support
  • (WIP) different resizeMode for Images support

I posted a demo on my Twitter

mrousavy avatar Dec 01 '20 22:12 mrousavy

I appreciate the detailed and quick response. I'm pretty deep into using React Navigation (and managed Expo), so this solution unfortunately won't work for me.

The video on your Twitter looks awesome though, congrats! Glad you were able to solve it for your app.

For now, I'll keep trying to figure this one out. I'd love to use it with native stack if possible.

nandorojo avatar Dec 01 '20 22:12 nandorojo

https://github.com/IjzerenHein/react-navigation-shared-element/issues/186#issuecomment-906179766 Regarding to @IjzerenHein it should be possible to implement shared animations with native stack now. I'm down to collect some money for his efforts and drop a donation. Would greatly appreciate contributions.

hirbod avatar Aug 26 '21 17:08 hirbod

IjzerenHein/react-navigation-shared-element#186 (comment) Regarding to @IjzerenHein it should be possible to implement shared animations with native stack now. I'm down to collect some money for his efforts and drop a donation. Would greatly appreciate contributors to this effort.

Likewise. I just commented there.

nandorojo avatar Aug 26 '21 17:08 nandorojo

I pushed the work I started some time ago concerning the shared element transitions: https://github.com/software-mansion/react-native-screens/pull/1089. It is just a draft version with some attempts to make the basic scenarios work on iOS. Feel free to use it for further development.

WoLewicki avatar Aug 27 '21 12:08 WoLewicki

Awesome awesome awesome.

wibb36 avatar Aug 29 '21 20:08 wibb36

Hi everyone! Due to the high interest in this feature, I've created a money-pool for collecting donations to build this feature. This resolves about leveraging react-native-shared-element to do the native shared-element transitions and extending react-navigation-shared-element with a createSharedElementNativeStackNavigator. https://paypal.me/pools/c/8Cyt3ED5wV

More info in this thread: https://github.com/IjzerenHein/react-navigation-shared-element/issues/186#issuecomment-906122873

IjzerenHein avatar Sep 01 '21 10:09 IjzerenHein

I donated $100! 🚀

nandorojo avatar Sep 27 '21 13:09 nandorojo

I donated $100 too, what's the status on this?

wibb36 avatar Sep 28 '21 01:09 wibb36

Not sure, @IjzerenHein is there a PR for the native-stack branch open? Or could you ping us when there is one?

nandorojo avatar Sep 28 '21 01:09 nandorojo

I donated $200 :P

Here is the branch with the current progress: https://github.com/IjzerenHein/react-navigation-shared-element/tree/native-stack

Quote from @IjzerenHein : "As far as progress, I've made a start but have not yet been able to spend any more time on it as I'm in the process of moving and also due to other work obligations. I'm expecting to resume work within the next 2 weeks."

This was a week ago, so he hopefully will continue in a week or so.

hirbod avatar Sep 28 '21 09:09 hirbod

Shared element transitions are currently under the development: https://github.com/software-mansion/react-native-screens/pull/1591

Closing as stale

kkafar avatar Nov 29 '22 15:11 kkafar