Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exit delay animation in react-transition-group

I am using react-transition-group in a ReactJS and GatsbyJS (V2) project.

I have my page transitions working with animation but when Link exit, the exiting animation is cut short because the next page is ready for entering.

I have tried delaying the Link action but whilst the page change was delayed, the exit animation is not triggered until the delay was over and the Link was actioned.

How can I delay the page change, whilst initiating the exiting animation onClick? Alternatively, is there a better way or props available?

Here is my code

Layout.js

class Layout extends React.Component {
  ...
  return (
    <Transition>{children}</Transition>
  );
}

Transition.js

class Transition extends React.Component {
constructor(props) {
    super(props);
    this.state = { exiting: false };
    this.listenerHandler = this.listenerHandler.bind(this);
  }

  listenerHandler() {
    this.setState({ exiting: true });
  }

  componentDidMount() {
    window.addEventListener(historyExitingEventType, this.listenerHandler);
  }

  componentWillUnmount() {
    window.removeEventListener(historyExitingEventType, this.listenerHandler);
  }

  static getDerivedStateFromProps({ exiting }) {
    if (exiting) {
      return { exiting: false };
    }
    return null;
  }

  render() {
    const transitionProps = {
      timeout: {
        enter: 0,
        exit: timeout
      },
      appear: true,
      in: !this.state.exiting
    };

    return (
      <ReactTransition {...transitionProps}>
        {status => (
          <div
            style={{
              ...getTransitionStyle({ status, timeout })
            }}
          >
            {this.props.children}
          </div>
        )}
      </ReactTransition>
    );
  }
}

export default Transition;

gatsby-config.js

import createHistory from 'history/createBrowserHistory';

const timeout = 1500;
const historyExitingEventType = `history::exiting`;

const getUserConfirmation = (pathname, callback) => {
  const event = new CustomEvent(historyExitingEventType, {
    detail: { pathname }
  });
  window.dispatchEvent(event);
  setTimeout(() => {
    callback(true);
  }, timeout);
};

let history;
if (typeof document !== 'undefined') {
  history = createHistory({ getUserConfirmation });
  history.block(location => location.pathname);
}

export const replaceHistory = () => history;

export { historyExitingEventType, timeout };

getTransitionStyle.js

const getTransitionStyles = timeout => {

return {
    entering: {
      transform: `scale(1.05) translateZ(0)`,
      opacity: 0
    },
    entered: {
      transition: `transform 750ms ease, opacity ${timeout}ms ease`,
      transitionDelay: `750ms`,
      transform: `scale(1) translateZ(0)`,
      opacity: 1
    },
    exiting: {
      transition: `transform 750ms ease, opacity ${timeout}ms ease`,
      transform: `scale(0.98) translateZ(0)`,
      opacity: 0
    }
  };
};

const getTransitionStyle = ({ timeout, status }) =>
  getTransitionStyles(timeout)[status];

export default getTransitionStyle;
like image 371
Darren Avatar asked Aug 10 '18 06:08

Darren


People also ask

How do I add a delay to a transition?

CSS Demo: transition-delayA value of 0s (or 0ms ) will begin the transition effect immediately. A positive value will delay the start of the transition effect for the given length of time. A negative value will begin the transition effect immediately, and partway through the effect.

What is unmountOnExit?

unmountOnExit. By default the child component stays mounted after it reaches the 'exited' state. Set unmountOnExit if you'd prefer to unmount the component after it finishes exiting. type: boolean. default: false.

How does React transition group work?

React Transition Group is not an animation library like React-Motion, it does not animate styles by itself. Instead it exposes transition stages, manages classes and group elements and manipulates the DOM in useful ways, making the implementation of actual visual transitions much easier.

How do you use stagger in React?

The trick for stagger animation is by delaying the animation by a certain amount gradually. For this gradual delay, we can use the item position inside the array as a delay metric. Hence, animation-delay becomes a multiple of array index for a given item with 100ms as per above example. And That's it!


1 Answers

Gatsby v2 is using Reach Router instead of React Router, so using getUserConfirmation with replaceHistory will not work anymore. In the Gatsby v2 RC you can use react-pose to create page transitions a little more straightforward:

gatsby-browser.js and gatsby-ssr.js:

import React from "react"
import Transition from "./src/components/transition"
 export const wrapPageElement = ({ element, props }) => {
  return <Transition {...props}>{element}</Transition>
}

transition.js component:

import React from "react"
import posed, { PoseGroup } from "react-pose"

const timeout = 250

class Transition extends React.PureComponent {
  render() {
    const { children, location } = this.props

    const RoutesContainer = posed.div({
      enter: { delay: timeout, delayChildren: timeout },
    })

    // To enable page transitions on mount / initial load,
    // use the prop `animateOnMount={true}` on `PoseGroup`.
    return (
      <PoseGroup>
        <RoutesContainer key={location.pathname}>{children}</RoutesContainer>
      </PoseGroup>
    )
  }
}

export default Transition

Inside your pages:

// Use `posed.div` elements anywhere on the pages.

const Transition = posed.div({
  enter: {
    opacity: 1,
  },
  exit: {
    opacity: 0,
  },
})

// ...

<Transition>Hello World!</Transition>

Check the official example for a working demo.

like image 133
Fabian Schultz Avatar answered Oct 19 '22 12:10

Fabian Schultz