Giter Site home page Giter Site logo

Animated transitions about react-router HOT 43 CLOSED

remix-run avatar remix-run commented on April 26, 2024 5
Animated transitions

from react-router.

Comments (43)

misterfresh avatar misterfresh commented on April 26, 2024 49

If like me you're just trying to get simple CSS transitions to work (especially fade-out) with react-router 2.0, and you don't need the heavy artillery, you can use "react-easy-transition", a very lightweight module that I wrote to solve that.

<EasyTransition 
    path={location.pathname}
    initialStyle={{opacity: 0}}
    transition="opacity 0.3s ease-in"
    finalStyle={{opacity: 1}}
>
    {this.props.children}
</EasyTransition>

The demo with redux integration

from react-router.

luandro avatar luandro commented on April 26, 2024 10

Check out the oficial example. There are alternatives to react-addons-css-transition-group such as velocity-react and react-motion.

Here's a simple example of a RouteTransition component, that will animate all it's children:

import React from 'react';
import {TransitionMotion, spring} from 'react-motion';

const willEnter = children => ({children, opacity: spring(0), translate: spring(999)});
const willLeave = (key, {children}) => ({children, opacity: spring(0), translate: spring(-999)});
const getStyles = (children, pathname) => ({[pathname]: {children, opacity: spring(1), translate: spring(0)}});

export default function RouteTransition({children, pathname}) {
    return (
        <TransitionMotion
            styles={getStyles(children, pathname)}
            willEnter={willEnter}
            willLeave={willLeave}
        >
            {interpolated =>
                <div>
                    {Object.keys(interpolated).map(key =>
                        <div
                            key={`${key}-transition`}
                            style={{
                                height: '100%',
                                width: '100%',
                                position: 'absolute',
                                opacity: interpolated[key].opacity,
                                transform: `translateX(${interpolated[key].translate}px)`
                            }}
                        >
                            {interpolated[key].children}
                        </div>
                    )}
                </div>
            }
        </TransitionMotion>
    );
}

Simply wrap and it's done:

<RouteTransition pathname={this.props.location.pathname} style={{visibility: 'hidden', width: '100%', height: '100%'}}>
   {this.props.children}
</RouteTransition>

from react-router.

AlastairTaft avatar AlastairTaft commented on April 26, 2024 7

None of these examples work with asynchronous routing, has anyone got any examples of that?

Looking for something that starts fading out before the next chunk has loaded.

from react-router.

joncursi avatar joncursi commented on April 26, 2024 6

Has anyone been able to crossfade with React Router? Every example I've seen will not fade in the new route until the old route is fully faded out, which flashes the screen in a jarring way. Looking for a crossfade solution!

from react-router.

AlastairTaft avatar AlastairTaft commented on April 26, 2024 6

I managed it in the end. Although for my use case I had a swiping transition. Things I learnt:

  • Don't use ReactTransitionGroup or ReactCSSTransitionGroup, these animations aren't interruptible, makes it feel clunky if the user navigates quickly. The componentWillEnter won't fire until it's componentWillLeave method has finished completing if the user navigates forward and then back to the same component (means they have to wait for the fade to complete)
  • Avoid using the asynchronous getComponent in react-router, seems like this isn't interruptible. I.e. if I navigate to a new page which calls getComponent and then the user presses back before getComponent resolves, they will end up with the wrong component loaded for the current url when getComponent does resolve. Didn't test this to much so could be wrong.
  • Side note: If you are experimenting with ReactTransitionGroup the lifecycle methods won't get called if you're using any sort of higher order component like react-redux's connect, which is a pretty common use case. For react-redux explicitly you can use their {withRef: true} parameter and then use getWrappedInstance, although it's the only escape hatch I know, I strongly dislike it, as it seems to break my seperation of concerns between Dumb and State components.

I went with react-motion, it's the only library that seems to exist that allows you to interrupt animations, even native css animations can't do it, seems animation on the web is seriously lacking, happy to be told I'm wrong.

I have an App component that renders all the child components (pages) within it. I added some logic (using redux) that would unmount the current page when a script chunk was loading, this would give me my page swipe out animation before the next chunk has started loading.

Then my navigation code looked something like this, i.e. the user would click a 'Next' button and it would fire the below code.

dispatch({
  type: 'UPDATE_SCRIPT_CHUNK_IS_LOADING',
  value: true,
})
// Webpack loading of the next chunk needed to display the next page
require.ensure([], (require) => {
  this.context.router.push(`Personal/Index`)
  dispatch({
    type: 'UPDATE_SCRIPT_CHUNK_IS_LOADING',
    value: false,
  })
}, "personal")  

The dispatch updates my app redux state so that my App component knows to unmount it's current page. When the next chunk has loaded 'script chunk loading' is turned off in state and the next page is animated in. react-motion takes care of the animating quite elegantly, although it took me a fair bit of time figuring out how their <TransitionMotion /> api worked.

The downside to this method, is the delay the user gets between clicking to navigate and the url in the address bar changing (although the unmount animation is instant). And having to wrap navigation points in chunk loading statements. I think this is the best we can do at the moment though.

from react-router.

AlastairTaft avatar AlastairTaft commented on April 26, 2024 3

I created a PanelsTransition component that would animate a panel in and out when the user navigates.

import React, { Component } from 'react'
import { TransitionMotion, spring, presets } from 'react-motion'
// These are just some convenience functions to help calculate the left position of the
// animation
import { getPanelWidth, calculatePanelToEdgeOfScreenDistance } from './../../Panel'

// The css just makes the leaving component `position:absolute` so that it doesn't 
// take up space in the document
require('./PanelsTransition.css')

const calculatePosition = (x, delta) => {
  // If we're not in a browser environment don't do anything
  if (typeof window === 'undefined') return 0;
  const dist = calculatePanelToEdgeOfScreenDistance()
  return dist * x * delta
};

class PanelsTransition extends Component {

  static propTypes = {
    /**
     * The direction of navigation, 1 for forwards, -1 for backwards
     */
    delta: React.PropTypes.number,

    /**
     * Set this to true to animate on initial render
     */
    animateOnInitialRender: React.PropTypes.bool,
  };

  static defaultProps = {
    animateOnInitialRender: false,
  };

  willLeave = () => {
    return { 
      transitionAmount: spring(-1, {stiffness: 300, damping: 26}),
      finalAmount: -1,
    }
  };

  willEnter = () => {
    return { 
      transitionAmount: 1,
      finalAmount: 1,
    }
  };

  render = () => {
    const { pathname, delta, animateOnInitialRender } = this.props

    // If it isn't an initial render then animate in the first component
    if (animateOnInitialRender){
      var defaultStyles = [{
        key: pathname,
        style: {
          transitionAmount: spring(1, presets.gentle),
          finalAmount: 1,
        },
        data: {
          component: this.props.children,
        },
      }]
    }

    return <TransitionMotion
      willLeave={this.willLeave}
      willEnter={this.willEnter}
      defaultStyles={defaultStyles}
      styles={[{
        key: pathname,
        style: {
          transitionAmount: spring(0, presets.gentle),
          finalAmount: 0,
        },
        data: {
          component: this.props.children,
        },
      }]}>
      {interpolatedStyles => {
        return <div>
          {interpolatedStyles.map(config => {
            const isLeaving = config.style.finalAmount == -1
            return <div 
              // Makes leaving components absolute so they don't take up space
              // in the document
              className={`PanelsTransition${isLeaving ? '-leaving' : ''}`}
              key={config.key} 
            >
              <div style={{
                position: 'relative',
                left: calculatePosition(config.style.transitionAmount, delta),
              }}>
                {config.data.component}
              </div>
            </div>
          })}
        </div>
      }}
    </TransitionMotion>
  }
}

export default PanelsTransition

In my App component I then had something like this

class App extends Component {

   render = () => {
       const { scriptChunkIsLoading } = this.props

       // I've got my routing state in redux and can figure out
       // the direction of navigation there but this can come from
       // anywhere or be passed in as props
       const state = this.context.store.getState()
       const { delta, initialRender } = state.routing

       var pathname = this.props.location.pathname
       // Ensure the path always starts with a slash, I've seen it be inconsistent
       // sometimes
       if (pathname.startsWith('/') == false)
          pathname = '/' + pathname

        // Make sure our child element has a key that relates to our page
        // this is important
        var child  = React.cloneElement(this.props.children, {
          key: pathname,
        })

        if (scriptChunkIsLoading){
           // This is kind of crude, it's just means we override our child element and
           // render an empty div that takes up space in the document, 
           // helps with the unmount animation. Only useful for me as a way of pushing
           // a footer element down the screen, rendering null would work just as well
           var child = <div 
              style={{
                height: (typeof document !== 'undefined') ? document.body.clientHeight : 1280
              }}
           />
           pathname = "animationPlaceholder"
        }

    return <div>
          ...
          <PanelsTransition 
            pathname={pathname} 
            delta={delta}
          >            
            {child}
          </PanelsTransition>
          ...
       </div>
   }
}

It doesn't work perfectly, I plan on improving it, the direction of the animation is wrong when the animation get's interrupted, but apart from that it seems to work well, the finalAmount bit is a bit of a hacky work in progress, that would likely change once I fix this up.

The react-motion api feels a bit awkward and that maybe I'm not using it the way it's intended especially with the way I've hijacked their data key to keep a reference to the component which then gets rendered, but hell it works.

from react-router.

luisrudge avatar luisrudge commented on April 26, 2024 2

I created a pen with a working example:
http://codepen.io/luisrudge/pen/QbEeOR

from react-router.

mjackson avatar mjackson commented on April 26, 2024 1

@iammerrick https://github.com/rackt/react-router/tree/master/examples/animations

from react-router.

taion avatar taion commented on April 26, 2024 1

The bundled animation example shows that.

from react-router.

coderberry avatar coderberry commented on April 26, 2024

@mjackson do you have any examples of doing this anywhere by chance?

from react-router.

mjackson avatar mjackson commented on April 26, 2024

@cavneb We're still working on this. I have a half-finished example, but I couldn't get it working.

from react-router.

dandean avatar dandean commented on April 26, 2024

I would also love this feature!

from react-router.

jbrantly avatar jbrantly commented on April 26, 2024

I was able to achieve this without issue with the React TransitionGroup (I'm using JS-based animations) by simply doing:

<TransitionGroup>
    <this.props.activeRouteHandler />
</TransitionGroup>

from react-router.

ryanflorence avatar ryanflorence commented on April 26, 2024

woah ... an unexpected side-effect of #90, @jbrantly would love for you to add a demo to examples/ :D

from react-router.

sophiebits avatar sophiebits commented on April 26, 2024

This actually would have worked before #90 I think, just put {this.props.activeRoute} inside the transition group.

from react-router.

toranb avatar toranb commented on April 26, 2024

+1 for the example mention :)

from react-router.

jbrantly avatar jbrantly commented on April 26, 2024

Sorry, I started to do this but didn't have the time to finish. Glad to see you got it done anyways!

from react-router.

ImDom avatar ImDom commented on April 26, 2024

Seems like wrapping <this.props.activeRouteHandler /> in <TransitionGroup> no longer works in version 0.6.x (and 0.7.x) unless you add addHandlerKey={true} to your Route.

Does anyone else have a better solution to this issue? Would be sad to lose out on the performance boost you get from not applying addHandlerKey={true}

from react-router.

ryanflorence avatar ryanflorence commented on April 26, 2024

There are necessarily two elements, otherwise the animation isn't possible. You need keys. The DOM diff just updates the changed parts of a node tree. If that's all you're doing, there is no previous element to animate out. Make sense?

from react-router.

ImDom avatar ImDom commented on April 26, 2024

Yeah, makes sense. Thanks :)

from react-router.

iammerrick avatar iammerrick commented on April 26, 2024

Would be awesome to see an example of react-router and transitions. :-)

from react-router.

iammerrick avatar iammerrick commented on April 26, 2024

Well, my good sir; I am embarrassed. Thanks!

from react-router.

mjackson avatar mjackson commented on April 26, 2024

@iammerrick np! :)

from react-router.

ryanflorence avatar ryanflorence commented on April 26, 2024

@iammerrick there is also this one :P http://jsbin.com/pamon/2

from react-router.

iammerrick avatar iammerrick commented on April 26, 2024

Thanks! :-)
On Mon, Oct 27, 2014 at 4:18 PM Ryan Florence [email protected]
wrote:

@iammerrick https://github.com/iammerrick there is also this one :P
http://jsbin.com/pamon/2


Reply to this email directly or view it on GitHub
#17 (comment).

from react-router.

romeovs avatar romeovs commented on April 26, 2024

so this won't work with animations between pages that are handled by the same handler?/users/john and /users/mike or do I just need to make sure the john and mike part are in the keys?

from react-router.

stefandraht avatar stefandraht commented on April 26, 2024

I tried building a version of the example using TransitionGroup's lifecycle hooks rather than CSSTransition and the hooks don't seem to get called. Am I missing something, or is this behavior only supposed to work with CSSTransitionGroup at the moment?

from react-router.

anthonator avatar anthonator commented on April 26, 2024

Is there a way or a pattern to make this more flexible? I don't necessarily want everything under a RouteHandler to transition.

from react-router.

luisrudge avatar luisrudge commented on April 26, 2024

Any guidance on this?

from react-router.

belohlavek avatar belohlavek commented on April 26, 2024

I'm interested in this too, current example seems outdated #1194

from react-router.

ryanflorence avatar ryanflorence commented on April 26, 2024

working on this in the 1.0 branch, there's no more RouteHandler stuff going
on, its all just props (props.children usually) and so animating between
routes should be identical to any other component that's animating
children. there will be much better documentation/examples for animation
too. thanks for your patience :)

On Tue, May 19, 2015 at 8:45 PM, Daniel Belohlavek <[email protected]

wrote:

I'm interested in this too, current example seems outdated #1194
#1194


Reply to this email directly or view it on GitHub
#17 (comment).

from react-router.

belohlavek avatar belohlavek commented on April 26, 2024

Hey @luisrudge thanks for your answer! I got that far myself but I got several warnings and an error when trying to run the code :(

Warning: Failed Context Types: Required context `routeDepth` was not specified in `RouteHandler`.
Warning: Failed Context Types: Required context `router` was not specified in `RouteHandler`.
Warning: owner-based and parent-based contexts differ (values: `undefined` vs `1`) for key (routeDepth) while mounting RouteHandler (see: http://fb.me/react-context-by-parent)
Warning: owner-based and parent-based contexts differ (values: `undefined` vs `function (props, context) [...long code...]

Uncaught TypeError: Cannot read property 'getRouteAtDepth' of undefined

I read somewhere that might have to do with having duplicate React instances. I'm using Browserify!

from react-router.

luisrudge avatar luisrudge commented on April 26, 2024

Never saw that. I'm using webpack, but this pen also doesn't throw this errors

Enviado pelo meu Windows Phone


De: Daniel Belohlavekmailto:[email protected]
Enviada em: ‎20/‎05/‎2015 00:27
Para: rackt/react-routermailto:[email protected]
Cc: Luís Rudgemailto:[email protected]
Assunto: Re: [react-router] Animated transitions (#17)

Hey @luisrudge thanks for your answer! I got that far myself but I got several warnings and an error when trying to run the code :(

Warning: Failed Context Types: Required context `routeDepth` was not specified in `RouteHandler`.
Warning: Failed Context Types: Required context `router` was not specified in `RouteHandler`.
Warning: owner-based and parent-based contexts differ (values: `undefined` vs `1`) for key (routeDepth) while mounting RouteHandler (see: http://fb.me/react-context-by-parent)
Warning: owner-based and parent-based contexts differ (values: `undefined` vs `function (props, context) [...long code...]

Uncaught TypeError: Cannot read property 'getRouteAtDepth' of undefined

I read somewhere that might have to do with having duplicate React instances. I'm using Browserify!


Reply to this email directly or view it on GitHub:
#17 (comment)

from react-router.

donaldpipowitch avatar donaldpipowitch commented on April 26, 2024

How is the progress with animations in 1.0? :)

from react-router.

sergeysova avatar sergeysova commented on April 26, 2024

googleplus-transition
I want to create that transition.
How can i do it with [email protected], [email protected] ?

from react-router.

irudoy avatar irudoy commented on April 26, 2024

@misterfresh huge thanks!

from react-router.

01dr avatar 01dr commented on April 26, 2024

@misterfresh many thanks!

from react-router.

sergio11 avatar sergio11 commented on April 26, 2024

thanks!!!!

from react-router.

donaldpipowitch avatar donaldpipowitch commented on April 26, 2024

Following the canonical example for animation I noticed that the leave animation is called when the new route could be resolved (e.g. when onEnter is async and finished its work by calling the callback). Say I switch from route-a to route-b and route-b fetches resources, which this takes 4 seconds nothing will be animated in these 4 seconds. The onLeave of route-a is called immediately however.

For me this is very surprising. Is there any way to trigger the leave animation for route-a immediately just like onLeave is called immediately for route-a?

from react-router.

donaldpipowitch avatar donaldpipowitch commented on April 26, 2024

@AlastairTaft That's gold! Especially the parts about what is interruptible and what not. Thank you. I also tried react-motion today and struggled with its API. Do you have an example for that, too?

from react-router.

stooboo avatar stooboo commented on April 26, 2024

We looked at a few libs out there for this
currently we're using
https://github.com/maisano/react-router-transition
Great so far ;-)

from react-router.

donaldpipowitch avatar donaldpipowitch commented on April 26, 2024

@stooboo react-router-transition sadly has the same limitation I mentioned earlier. I tried it today.

from react-router.

stooboo avatar stooboo commented on April 26, 2024

@donaldpipowitch cheers I missed that - thanks for the heads-up

from react-router.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.