lightrow / react-native-shared-element-orchestrator

Helper library for react-native-shared-element
1 stars 0 forks source link

Shared Transition Orchestrator

A helper library for react-native-shared-element. Works as a standalone animator or can be coupled with any navigation library.

npm

How it works

When new scene becomes active, orchestrator will check if there are matching shared elements (by ids) between new scene and previously active scene. For each found element it will create a transition. Additionally the scenes can animate themselves during scene transition (using sceneInterpolator prop). When scene is deactivated, orchestrator will try to find the previously active scene, and repeat the same process.

Example GIF

SharedTransitionOrchestrator

Provides context for scenes, observes scenes changes, renders transitions between scenes. By default it stretches to parent container, as it is meant to be inserted somewhere at the root app level, but it can also be placed somewhere else and styled accordingly.

Props
style? default {...StyleSheet.absoluteFillObject, zIndex: 99999999}
duration? default 500
Scene transition duration.
easing? default Easing.out(Easing.exp)
Scene transition easing function.
useNativeDriver? default true
Change to false if your style interpolators require it.
animation? default move
SharedElementAnimation
resize? default auto
SharedElementResize
align? default auto
SharedElementAlign


SharedTransitionScene

Provides context for elements, observes elements changes, optionally animates itself during scene transitions

Props
style? default undefined
Outer View style.
containerStyle? default undefined
Inner View style, sceneInterpolator applies to this one.
isActive? default false

true - Scene is added to the top of the active scenes stack.
false - Scene is removed from the active scenes stack.

Changing this value will trigger Orchestrator search for previous scene and matching elements to run transitions. Normally this stays true for all Scenes, and is flipped to false before Scene is unmounted to trigger reverse animations
sceneInterpolator? default undefined
Allows animating Scene transitions, e.g:
sceneInterpolator={(progress) => ({ opacity: progress })}
mountActivationDebounce? default 100
How long to wait for elements to mount, before allowing scene to activate. This delays the scene's appearance, but gives time for all SharedElements to mount and layout to settle. This is not always needed, so you can try setting it to 0 if activation delay is unwanted. The default value 100 is enough for <FlatList/> with initialScrollIndex set, like in the example project
onTransitionEnd? default undefined
Will be called for activated scene when transition fully ends (progress becomes 1)


SharedTransitionElement

Wraps the views you want to apply shared transition to.

Props
id required
ID used for matching Elements between Scenes
style? default undefined
View style.
zIndex? default 0
zIndex of transition that will run for this element. Used for controlling how transitions overlay each other, e.g. when Image has an Icon on top of it, both would be wrapped with different SharedTransitionElements with Icon having a higher zIndex


How to use /// WIP

npm i react-native-shared-element react-native-shared-element-orchestrator react-native-screens

# if iOS

cd ios
pod install
const App = () => {
  ...
  return (
      <SharedTransitionOrchestrator>
        <Gallery .../>
        {selectedMedia && <MediaViewer .../>}
      </SharedTransitionOrchestrator>
  )
}

const Gallery = () => {
  return (
    <SharedTransitionScene isActive>
      {media.map(mediaUrl => {
        return (
          <SharedTransitionElement id={mediaUrl}>
            ...
          </SharedTransitionElement>
        )
      })}
    </SharedTransitionScene>
  )
}

const MediaViewer = () => {
  const [isActive, setIsActive] = useState(true);

  const dismiss = async () => {
    setIsActive(false);
    setTimeout(() => {
      // allow view to close with transition before unmounting
      clearSelectedMedia();
    }, ANIMATION_DURATION)
  }

  return (
    <SharedTransitionScene
      sceneInterpolator={(progress) => ({ opacity: progress })}
      isActive={isActive}
      style={{
        backgroundColor: 'black',
        ...StyleSheet.absoluteFillObject
      }}
    >
      <SharedTransitionElement id={selectedMediaUrl}>
        ...
      </SharedTransitionElement>
    </SharedTransitionScene>
  )
}

Usage with navigation libraries is also possible. Scenes can be placed inside screens, and sceneInterpolator can be left empty. isActive can then be toggled off when the screen is being popped out of the screen stack, e.g. beforeRemove event in React Navigation