chenglou / react-motion

A spring that solves your animation problems.
MIT License
21.69k stars 1.16k forks source link

Transition between routes #130

Open codepunkt opened 9 years ago

codepunkt commented 9 years ago

Is there a way to use this with react-router to transition between components?

e.g. implement a page transition like http://www.semplicelabs.com/ with additional stagger on the menu items?

chenglou commented 9 years ago

Something like this? https://gist.github.com/maisano/8faf4bd123fa9842f4ea

codepunkt commented 9 years ago

I'm afraid i don't know how to use that. Tried using it as a parent route component in react-router@1.0.0-beta3, didn't work too well.

chenglou commented 9 years ago

@maisano care to help a bit here please?

nkbt commented 9 years ago

@gonsfx http://in-flux.github.io/component-router/example ?

maisano commented 9 years ago

oof, i should really add some usage docs to that gist.

@gonsfx the RouteTransition component was written not to be a route component, but a wrapper for rendering children. e.g.:

// in your base route component:
render() {
  return (
    <div>
      {/** ... **/}
      <RouteTransition pathname={this.props.location.pathname}>
        {this.props.children}
      </RouteTransition>
    </div>
  );
}
cheapsteak commented 9 years ago

Does that mean all children will animate in the same way? (can't have one child come in from the bottom, another from the right)

maisano commented 9 years ago

in this implementation of <RouteTransition>, all children being added will animate the same. the same goes for children being removed. however, it would be pretty simple to do some path comparisons and pull from a series of preset transition values. you could also pass in the history action for a particular transition.

codepunkt commented 9 years ago

@chenglou Thanks for pointing me in the right direction @maisano Thanks for your clarification - that works okay-ish but is a good starting point for further inquiry.

Followup questions:

boyswan commented 9 years ago

Would we able to see an example using the new api? Struggling to get my head around the changes!

chenglou commented 9 years ago

@boyswan not too familiar with the router. I'll defer to @maisano for this one

maisano commented 9 years ago

@boyswan by new api i'm assuming you meant the new react-motion api. i just updated the gist to reflect those changes, though i haven't tested it. conceptually it's largely the same, though a prop's name changed and you no longer need to access the val key from the interpolated values.

boyswan commented 9 years ago

Perfect, thanks for this!

mattapperson commented 9 years ago

@maisano I seem to be having the same issue as@gonsfx where the initial route does not load...

maisano commented 9 years ago

@mattapperson the initial route doesn't load or doesn't transition? if it's the latter, i think you can get that by adding defaultStyles.

mattapperson commented 9 years ago

@maisano it was an issue with my styles. the absolute included in this component broke stuff

maisano commented 9 years ago

@mattapperson :+1: yea, that component is more of a demo than anything else.

rclai commented 9 years ago

Would the implementation change dramatically if @chenglou's example was modified to make the transition be a horizontal sliding animation instead of a fading one?

griable commented 9 years ago

Hi @rclai,

Just replace the scale transfom in the render by translateX(``${interpolated[key].x}%``).

And replace scale in both willEnter and willLeave methods by respectively x: spring(-100) and x: spring(100).

That should work.

cdebotton commented 8 years ago

Perhaps I'm missing something, but I'm having an issue with repeating willEnter and willLeave animations for routes. I'm assuming that because is retaining the keys of each route, when you visit pages the second time, the willLeave animation hook fires on willEnter. Any guidance on this would be greatly appreciated, thank you! I think this library is fantastic but this is probably the one issue keeping me from implementing it on a project. I know that I could use the route key from react-router@2, but I need to animate nested routes based on the path segment specifically, and it would be ideal if I could use that portion of the path.

const routerStyles = props => {
  const { pathKey, children } = props;

  return {
    [pathKey]: {
      opacity: spring(1),
      scale: spring(0),
      position: 'relative',
      top: spring(0),
      children,
    },
  };
};

const routeWillEnter = (_, { children }) => {
  return {
    opacity: spring(0),
    scale: spring(1.2),
    position: 'absolute',
    top: 0,
    children,
  };
};

const routeWillLeave = (_, { children }) => {
  return {
    opacity: spring(0),
    scale: spring(0.9),
    position: 'absolute',
    top: 0,
    children,
  };
};

const RouterTransition = props => {
  return (
    <TransitionMotion
      styles={routerStyles(props)}
      willEnter={routeWillEnter}
      willLeave={routeWillLeave}
    >
      {interpolated => (
        <span>
          {Object.keys(interpolated).map(key => {
            const {
              children,
              opacity,
              top,
              scale,
              position,
            } = interpolated[key];

            return (
              <span
                key={`${key}-transition`}
                style={{
                  position,
                  top,
                  opacity,
                  transform: `scale(${scale})`,
                }}
                children={children}
              />
            );
          })}
        </span>
      )}
    </TransitionMotion>
  );
};
maisano commented 8 years ago

@cdebotton it's hard to tell what's wrong from this–what's the usage look like?

cdebotton commented 8 years ago

@maisano, sure, the implementation looks like this:

  render(): any {
    const { location, ...props } = this.props;
    const pathKey = this.props.location.pathname.replace(/^\//, '')
      .split('/').shift();

    return (
      <div
        style={{
          backgroundColor: this.props.backgroundColor,
          color: this.props.foregroundColor,
        }}
        className={styles.app}
      >
        <div
          ref={c => this._container = c}
          style={{
            backgroundColor: this.props.backgroundColor,
            color: this.props.foregroundColor,
          }}
          className={styles.container}
        >
          <Nav
            style={{
              zIndex: this.props.video ? 1000 : 0,
            }}
          />

          /** Implementation begins here **/

          <RouterTransition pathKey={pathKey}>
            {this.props.children}
          </RouterTransition>

          /** Implementation ends here **/

        </div>
      </div>
    );
cdebotton commented 8 years ago

Sorry for so many posts, but I've at least solved for when the problem happens. If I am transitioning between routes, and then click into another route, the leave animation doesn't fire on the route that is already on the DOM. I believe that this is because the key has not been removed yet from the styles configuration object.

When I wait for the transitions to complete, everything behaves as expected.

I know that I could add a counter, or a constantly changing key to the key, but that feels hacky to me.

maisano commented 8 years ago

i'm still a little unclear on what the problem you're having is, but based on animations repeating, i would guess it's because you're potentially clobbering the pathKey for multiple routes (e.g. you're passing in the same pathKey for matches at "/foo/bar" and "/foo/baz"). why split on slash and return the first element instead of the entire pathname?

abelovic commented 8 years ago

How to get this to work with 0.4.* ? TransitionMotion's api changed and I cannot get this example to work anymore :(

chenglou commented 8 years ago

@threepointone we should have one, authoritative answer on how to make RR work with RM. I myself haven't used RR so I can't answer. But this is a recurring question.

@abelovic the migration guide is here. Maybe this will help

threepointone commented 8 years ago

yes, we should have an example (indeed, we should also prep an example for their incoming v2 release). I'll start hacking on this today.

andrewgleave commented 8 years ago

This implementation uses 0.4.2 and is used as a wrapper component which takes – amongst other things – Route components.

This is just a simple example, and fades a single component in from 0.75 opacity.

Hope this helps someone.

import React from 'react';

import Pure from 'mixins/Pure';
import { TransitionMotion, spring } from 'react-motion';

const SpringModel = { stiffness: 250, damping: 25, precision: 0.05 };

const XYZTransitionContainer = React.createClass({
  mixins: [Pure],
  propTypes: {
    id: React.PropTypes.string.isRequired,
    height: React.PropTypes.number.isRequired
  },
  willEnter() {
    return { opacity: 0.75 };
  },
  willLeave() {
    return { opacity: spring(1, SpringModel) };
  },
  getStyles() {
    const { id, children } = this.props;
    return [{
      key: id,
      style: { opacity: spring(1, SpringModel) },
      data: { children }
    }];
  },
  render() {
    const { height } = this.props;
    return (
      <TransitionMotion
        styles={ this.getStyles }
        willEnter={ this.willEnter }
        willLeave={ this.willLeave }>
        { values =>
          <div style={{ height }}>
            { values.map(({ key, style, data }) =>
              <div
                key={ key }
                style={{
                  height,
                  opacity: style.opacity
                }}>
               { data.children }
             </div>
            )}
          </div>
        }
      </TransitionMotion>
    );
  }
});

export default XYZTransitionContainer;
chenglou commented 8 years ago

FYI, the newest example in the Wiki Gallery is a RM wrapper for RR.

adamscybot commented 8 years ago

I also saw this posted on twitter today: https://github.com/nanot1m/motions

abelovic commented 8 years ago

@andrewgleave - thanks for this! I started doing something like this (except I used a slide transition) and it worked. After maisano released his wrapper I ended up changing to that because it is nicely abstracted

@maisano- Great wrapper!!! I only ran into one issue/improvement which I posted in your repo

Fade in/out:

Unfortunately this is one effect that doesn't look right. I have tried different stiffness/damping settings but when you transition it either moves so quick you don't notice the fade or one page starts to show through the other since it's opacity hasn't set zero yet. Ideally we would want the current route to fade out and once that is complete the new route fade in. I suspect you would need an animation end event to do this (which I believe isn't available yet)? Maybe there is another way to achieve this?

@chenglou - Obviously incredible library but thanks for helping lead us to solutions on how to integrate with major third party components! Your demos are very cool but being able to use react-motion with react-router for example is a big deal. Developers finally have a well designed library to integrate advanced animations in real world apps :)

Expanding upon this concept further doing the following with react-motion would be awesome!

https://www.google.com/design/spec/animation/meaningful-transitions.html

Although maybe this belongs in react-motion-ui-pack :)

silouanwright commented 8 years ago

Just to note (noted it on the gist a couple of months back): this uses absolute positioning to work. If you don't have the routeTransition around the footer, it won't look correct visually as whatever you wrap the RouteTransition around will go below your footer.

Basically: don't expect to have a static header / footer, and do a fade transition on the content between the header and footer...

maximusnikulin commented 7 years ago

Is there some way to avoid position:absolute ? Maybe we would be able to use some callback to fire when leaving finishes and run enter transition right after that?

maximusnikulin commented 7 years ago

It's possible to make this work without position:absolute, but it not so reliable solution yet. Maybe it will be usefull. https://github.com/chenglou/react-motion/issues/419