Page Transitions Without React-Router

page transitions without React-Router

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>

How to add page transitions to React without using the router?

I figured it out! Your CSS animations are trying to use fadeIn, but that's not a CSS property. You need to change it to opacity. Like so:

//Page transition
.pageTransition-enter {
opacity: 0.01;
}
.pageTransition-enter.pageTransition-enter-active {
animation: opacity 1s ease-in;
}
.animation-leave {
opacity: 1;
}
.pageTransition-leave.pageTransition-leave-active {
animation: opacity 3s ease-in;
}
.pageTransition-appear {
opacity: 0.01;
}
.pageTransition-appear.pageTransition-appear-active {
animation: opacity 5s ease-in;
}

React router dom v6 prevent transition without Prompt

You can but it requires using a custom history object and HistoryRouter. For this history@5 needs to be a package dependency.

  1. Import version 5 of history.

    npm i -S history@5
  2. Create and export a custom history object.

    import { createBrowserHistory } from 'history';

    export default createBrowserHistory();
  3. Import and render a HistoryRouter and pass history as a prop.

    ...
    import { unstable_HistoryRouter as Router } from "react-router-dom";
    import history from './history';
    ...

    <Router history={history}>
    <App />
    </Router>
  4. Follow the docs for blocking transitions.

    // Block navigation and register a callback that
    // fires when a navigation attempt is blocked.
    let unblock = history.block((tx) => {
    // Navigation was blocked! Let's show a confirmation dialog
    // so the user can decide if they actually want to navigate
    // away and discard changes they've made in the current page.
    let url = tx.location.pathname;
    if (window.confirm(`Are you sure you want to go to ${url}?`)) {
    // Unblock the navigation.
    unblock();

    // Retry the transition.
    tx.retry();
    }
    });

Demo:

Edit agitated-blackwell-dpl9b3

Transition page content excluding the header and footer

Assuming your header and footer is the same for all the pages, you should move the header and footer out of your Page components, and out of the <AnimatePresence> component.

<Header />
<AnimatePresence initial={false} exitBeforeEnter>
<Switch location={location} key={location.pathname}>
{/* Only the main content gets animated. No headers / footers in here. */}
</Switch>
</AnimatePresence>
<Footer />

Transition between routes in react-router-dom v6.3

I think your solution is close to working. Move the PageLayout component and motion.div into a layout route that uses the current path as a React key.

Example:

import { Outlet } from 'react-router-dom';

const AnimationLayout = () => {
const { pathname } = useLocation();
return (
<PageLayout>
<motion.div
key={pathname}
initial="initial"
animate="in"
variants={pageVariants}
transition={pageTransition}
>
<Outlet />
</motion.div>
</PageLayout>
);
};

...

<Routes>
<Route element={<AnimationLayout />}>
<Route path="/audit" element={<Audit />} />
<Route path="/smartx" element={<SmartX />} />
<Route path="/faq" element={<FAQ />} />
<Route path="/support" element={<Support />} />
<Route path="/terms" element={<Terms />} />
<Route path="/policy" element={<Policy />} />
<Route path="/about" element={<About />} />
<Route path="*" element={<Navigate to="/audit" replace />} />
</Route>
</Routes>

Edit transition-between-routes-in-react-router-dom-v6-3

react-transition-group SwitchTransition doesn't work on first update but on all others

To answer my own question: I couldn't figure out the reason, but I suppose the problem had to do with the key property of the transition component. I just passed in the entire page (state) object, and apparently, that causes problems. When I used a subset of the object as key (like page.id, or pages[0].id) it started working correctly in all instances.



Related Topics



Leave a reply



Submit