|
# Lets say we've got Screen A and Screen B, and we want to make a shared transition from A to B. |
|
# We first create a 'manager', or a 'service', call it however you like :) It's a simple class: |
|
|
|
class SomeAnimationManager { |
|
constructor(navigation) { |
|
// We pass the navigation bc we'd want to create transitions from the manager, |
|
// as it's also the one responsible for the animations |
|
this.navigation = navigation; |
|
|
|
// This is going to be a reference to the original element we're 'sharing' |
|
this.originalElementRef = null; |
|
|
|
// Here we're going to save the original element dimensions, to be used |
|
// as the 'starting point' in screen B |
|
this.initialDimensions = null; |
|
|
|
// This is an example of an animated value we're going to pass to the shared element in screen B |
|
this.translateY = null; |
|
} |
|
|
|
// Here we set the original element ref |
|
setOriginalElementRef = (ref) => this.originalElementRef = ref; |
|
|
|
// This is the function that would be called when we want to navigate |
|
beginTransition = () => { |
|
this.originalElementRef.measureInWindow((x, y, width, height) => { |
|
this.initialDimensions = { x, y, width, height }; |
|
|
|
// Now that we have the initial dimensions, we can navigate to screen B, and pass it |
|
// the reference to this animation manager |
|
this.navigation.push('B', { animationManager: this }); |
|
}) |
|
}; |
|
|
|
// This method is called from Screen B, before it mounts. Here we ALREADY have the initial dimensions, |
|
// needed for the transition. |
|
getInitialAnimationValues = () => { |
|
const { y } = this.initialDimensions; |
|
|
|
// Now, our initial animated value is the position of the shared element on screen A, |
|
// and we pass it to screen B |
|
this.translateY = new Animated.Value(y); |
|
|
|
return { |
|
transform: [{ translateY: this.translateY }] |
|
} |
|
} |
|
|
|
// Called from screen B, and changes the values of the animation you want to perform. |
|
// This object is the one we passed to screen B, and when changing this.translateY, shared element will animate accordingly |
|
// because its style has a reference to this Animated object. |
|
animateIntro = () => { |
|
Animated.timing( |
|
this.translateY, |
|
{ toValue: 0 } |
|
).start(); |
|
} |
|
} |
|
|
|
------------- |
|
|
|
# This is screen A, a very simplified version of it, with a SharedElemenet: |
|
|
|
class ScreenA extends React.Component { |
|
constructor() { |
|
// Here we assign the animation manager in Screen A |
|
this.animationManager = new SomeAnimationManager(this.props.navigation); |
|
} |
|
|
|
|
|
render() { |
|
<View> |
|
// Here we set the reference to the original element, inside the animation manager |
|
<SharedElement ref={this.animationManager.setOriginalElementRef}/> |
|
</View> |
|
} |
|
} |
|
|
|
--------------- |
|
|
|
# Screen B, which takes everything it needs from the manager: |
|
|
|
class ScreenB extends React.Component { |
|
constructor() { |
|
// Here we get the reference to the animation manager which we passed FROM the animation |
|
// manager, and it's the same one used in Screen A |
|
this.animationManager = this.props.navigation.getParam('animationManager'); |
|
|
|
// This method will init all of the animations needed for the transition |
|
this.initAnimatedValues(); |
|
} |
|
|
|
initAnimatedValues = () => { |
|
// We get the style object for our shared element, and the manager's going to |
|
// be responsible for its animation |
|
this.animatedStyle = this.animationManager.getInitialAnimationValues(); |
|
}; |
|
|
|
// When Screen B is mounted, it already has the initial animated style with initial values from A. |
|
// And now we tell the animation manager to actually animate. |
|
componentDidMount() { |
|
this.animationManager.animateIntro(); |
|
} |
|
|
|
render() { |
|
return ( |
|
<SharedElement style={this.animatedStyle}/> |
|
) |
|
} |
|
} |