Skip to content

Instantly share code, notes, and snippets.

@portons
Created January 30, 2019 06:33
Show Gist options
  • Select an option

  • Save portons/ee2c0fa02e6e07593a6b6c6e053b99c8 to your computer and use it in GitHub Desktop.

Select an option

Save portons/ee2c0fa02e6e07593a6b6c6e053b99c8 to your computer and use it in GitHub Desktop.

Revisions

  1. Vladimir Porton created this gist Jan 30, 2019.
    110 changes: 110 additions & 0 deletions shared-transition.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,110 @@
    # 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}/>
    )
    }
    }