page transitions without React-Router

安稳与你 提交于 2021-02-18 11:14:10

问题


This should be so simple, but I've been breaking my head over this for days. I'm trying to animate my page transitions. The problem is the docs SUCK. I've followed them over and over and tried every which way, but can't get it to work.

What I want to do is slide my pages gracefully either right or left, and fade the one that is unmounting gracefully out behind it. Simple right? I am NOT using React Router for my pages.

I've tried a variety of solutions for this, but the problem seems to be on the unmounting. When the page is replaced, the existing page gets unmounted before it can transition out. I'm posting my attempt with react-transition-group, though at this point, I'll accept any other solution that works. I'm not sure react-transition-group is being actively maintained actually, because there are numerous other postings for help with 0 responses.

So on my app container I want to put something like this:

<App>
  <PageSlider>
     <Page key={uniqueId} />  <==this will be "swapped" through (Redux) props
  </PageSlider>

So, from what I've read, I have to use a TransitionGroup container as my PageSlider for this, so that it will manage the entering and exiting of my page. So here goes:

 class PageSlider extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <TransitionGroup 
         component="div"
         id="page-slider"
         childFactory={child => React.cloneElement(child,
           {classNames: `page-${this.props.fromDir}`, timeout: 500}
         )}
      >
        {this.props.children}
      </TransitionGroup>

    );
  }
}

I also read I need to do a "child Factory" to enable the exiting stuff. There was absolutely no example of this I could find in the docs. Since the pages will come from different directions, I will pass to this the direction from which I want to slide the page, which will tell the page what class it gets.

Now, as for the page itself, I have wrapped it in a CSSTransition like so. There were no good examples in the docs of how this all gets passed down, so I'm really confused what to do here:

 class Page extends Component {
  constructor(props) {
    super(props);
  }

  render() {
   return (
    <CSSTransition>                   <==????????
      {this.props.children}           Do props get passed down?
    </CSSTransition>                  Which ones? 
 );                                   Does "in" get taken care of?
  }
}

And just to finish the styles will be applied in CSS something like this:

.page {
  display: flex; 
  flex-direction: column; 
  height: 100%;
  position: absolute;
  top: 0;
  bottom: 0;
  -webkit-transition: all 500ms ease-in-out;
  transition: all 500ms ;
}

//go from this
.page-right-enter {
  -webkit-transform: translate3d(100%, 0, 0);
  transform: translate3d(100%, 0, 0);
}

//to this
.page-right-enter-active {
  -webkit-transform: translate3d(0, 0, 0);
  transform: translate3d(0, 0, 0);
}

 //exiting go from this
    .page-right-exit {
      opacity: 1;
    }
   //to this
    .page-right-exit-active {
      opacity: 0;
    }

All of these components will be connected through Redux so they know when a new page has been triggered and which direction has been called.

Can someone PLEEEEASE help me on this? I've literally spent days and tried every library out there. I'm not wedded to react-transition-group! Any library that works on the unmount I'll try. Why is this not easier?


回答1:


OK. Well, I struggled with this for WAAAAY too long. I finally dumped react-transition-group, and went pure CSS. Here's my solution.

PageSlider.js

import React, { Component } from 'react';
import PropTypes from 'prop-types';

require('./transitions.scss');

const BlankPage = (props) => <div className="placeholder"></div>;

class PageSlider extends Component {
  constructor(props) {
    super(props);
    this.state = {
       nextRoute: props.page,
       pages: {
        A: {key: 'A', component: BlankPage, className: 'placeholder'},
        B: {key: 'B', component: BlankPage, className: 'placeholder'},
       },
       currentPage: 'A'
    };
  }

  componentDidMount() {
    //start initial animation of incoming
    let B = {key: 'b', component: this.state.nextRoute, className: 'slideFromRight'}; //new one
    let A = Object.assign({}, this.state.pages.A, {className: 'slideOutLeft'}); //exiting
    this.setState({pages: {A: A, B: B}, currentPage: 'B'});    
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.page != this.state.nextRoute) {
      this.transition(nextProps.page, nextProps.fromDir);
    }
  }

  transition = (Page, fromDir) => {
    if (this.state.nextRoute != Page) {
       let leavingClass, enteringClass;
       let pages = Object.assign({}, this.state.pages);
       const current = this.state.currentPage; 
       const next = (current == 'A' ? 'B' : 'A');
       if (fromDir == "right") {
         enteringClass = 'slideFromRight';
         leavingClass = 'slideOutLeft';
       } else {
         enteringClass = 'slideFromLeft';
         leavingClass = 'slideOutRight';
       }
       pages[next] = {key: 'unique', component: Page, className: enteringClass};
       pages[current].className = leavingClass;
       this.setState({pages: pages, nextRoute: Page, currentPage: next});  
    }
  }


  render() {
    return (
      <div id="container" style={{
        position: 'relative', 
        minHeight: '100vh',
        overflow: 'hidden'
      }}>
        {React.createElement('div', {key: 'A', className: this.state.pages.A.className}, <this.state.pages.A.component />)}
        {React.createElement('div', {key: 'B', className: this.state.pages.B.className} , <this.state.pages.B.component />)}
       </div>
  );
  }

}


PageSlider.propTypes = {
  page: PropTypes.func.isRequired,
  fromDir: PropTypes.string.isRequired
};

export default PageSlider;

transition.scss

.placeholder {
    position: absolute;
    left: 0;
    width: 100vw;
    height: 100vh;
    background: transparent;
    -webkit-animation: slideoutleft 0.5s forwards;
    -webkit-animation-delay: 10;
    animation: slideoutleft 0.5s forwards;
    animation-delay: 10;
}



.slideFromLeft {
    position: absolute;
    left: -100vw;
    width: 100vw;
    height: 100vh;
    -webkit-animation: slidein 0.5s forwards;
    -webkit-animation-delay: 10;
    animation: slidein 0.5s forwards;
    animation-delay: 10;
}


.slideFromRight {
    position: absolute;
    left: 100vw;
    width: 100vw;
    height: 100vh;
    -webkit-animation: slidein 0.5s forwards;
    -webkit-animation-delay: 10;
    animation: slidein 0.5s forwards;
    animation-delay: 10;;
}

.slideOutLeft {
    position: absolute;
    left: 0;
    width: 100vw;
    height: 100vh;
    -webkit-animation: slideoutleft 0.5s forwards;
    -webkit-animation-delay: 10;
    animation: slideoutleft 0.5s forwards;
    animation-delay: 10;
}

.slideOutRight {
    position: absolute;
    left: 0;
    width: 100vw;
    height: 100vh;
    -webkit-animation: slideoutright 0.5s forwards;
    -webkit-animation-delay: 10;
    animation: slideoutright 0.5s forwards;
    animation-delay: 10;
}

@-webkit-keyframes slidein {
    100% { left: 0; }
}

@keyframes slidein {
    100% { left: 0; }
}

@-webkit-keyframes slideoutleft {
    100% { left: -100vw; opacity: 0 }
}

@keyframes slideoutleft {
    100% { left: -100vw; opacity: 0}
}
@-webkit-keyframes slideoutright {
    100% { left: 100vw; opacity: 0}
}

@keyframes slideoutright {
    100% { left: 100vw; opacity: 0}
}

Passing in the next component, which is my react Page component, called like so:

app.js

 <div id="app">
      <PageSlider page={this.state.nextRoute} fromDir={this.state.fromDir}/>
   </div>


来源:https://stackoverflow.com/questions/51459763/page-transitions-without-react-router

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!