简体   繁体   中英

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;

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM