reactjsreact-transition-group

React Transition Group Dynamically Change Slide Direction


I'm creating a mobile navigation menu and using CSSTransition component from React Transition Group to handle animating in the different levels of my navigation.

I am able to successfully animate in different levels but only in one direction. For instance, the content will enter in from the right and exit to the left. The part that is confusing to me is when i need to change the direction that the content animates in or out.

For my example lets say I have 3 slides

Initially slide #2 would enter in from the right If we go to slide #3 it would exit left.

If we are on slide #3 and want to go back to slide #2 then I want slide #2 to enter in from the right.

import { useState } from 'react';
import { CSSTransition } from 'react-transition-group';
import './App.css';

const App = () => {
    const [page, setPage] = useState(1);
    return (
        <>
            Page: {page}
            <nav>
                <button onClick={() => page !== 1 && setPage(page - 1)}>Prev</button>
                <button onClick={() => page >= 1 && page <= 2 && setPage(page + 1)}>
                    Next
                </button>
            </nav>
            <div className='content'>
                <CSSTransition
                    in={page === 1}
                    classNames='slide'
                    timeout={500}
                    unmountOnExit
                >
                    <SlideOne />
                </CSSTransition>
                <CSSTransition
                    in={page === 2}
                    classNames='slide'
                    timeout={500}
                    unmountOnExit
                >
                    <SlideTwo />
                </CSSTransition>
                <CSSTransition
                    in={page === 3}
                    classNames='slide'
                    timeout={500}
                    unmountOnExit
                >
                    <SlideThree />
                </CSSTransition>
            </div>
        </>
    );
};

const SlideOne = () => {
    return <h3>Hello From Slide One</h3>;
};

const SlideTwo = () => {
    return <h3>Hello From Slide Two</h3>;
};

const SlideThree = () => {
    return <h3>Hello From Slide Three</h3>;
};

export default App;
.content {
    width: 200px;
    height: 100px;
    overflow: hidden;
    box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.1);
    padding: 1rem;
    position: relative;
}

h3 {
    position: absolute;
}

.slide-enter {
    opacity: 0;
    transform: translateX(100%);
}
.slide-enter-active {
    opacity: 1;
    transform: translateX(0%);
    transition: all 0.5s;
}
.slide-exit {
    opacity: 1;
    transform: translateX(0%);
}
.slide-exit-active {
    opacity: 0;
    transform: translateX(-100%);
    transition: all 0.5s;
}


Solution

  • import { useState } from "react";
    import { CSSTransition, TransitionGroup } from "react-transition-group";
    import "./App.css";
    
    const App = () => {
      const [page, setPage] = useState(1);
      const [direction, setDirection] = useState("left");
      return (
        <>
          Page: {page}
          <nav>
            <button
              onClick={() => {
                page !== 1 && setPage(page - 1);
                direction !== "right" && setDirection("right");
              }}
            >
              {" "}
              Prev
            </button>
            <button
              onClick={() => {
                page >= 1 && page <= 2 && setPage(page + 1);
                direction !== "left" && setDirection("left");
              }}
            >
              Next
            </button>
          </nav>
          <div className="content">
            {/* <TransitionGroup> */}
            <CSSTransition
              in={page === 1}
              classNames={`slide-${direction}`}
              timeout={500}
              unmountOnExit
            >
              <SlideOne />
            </CSSTransition>
            <CSSTransition
              in={page === 2}
              classNames={`slide-${direction}`}
              timeout={500}
              unmountOnExit
            >
              <SlideTwo />
            </CSSTransition>
            <CSSTransition
              in={page === 3}
              classNames={`slide-${direction}`}
              timeout={500}
              unmountOnExit
            >
              <SlideThree />
            </CSSTransition>
            {/* </TransitionGroup> */}
          </div>
        </>
      );
    };
    
    const SlideOne = () => {
      return <h3>Hello From Slide One</h3>;
    };
    
    const SlideTwo = () => {
      return <h3>Hello From Slide Two</h3>;
    };
    
    const SlideThree = () => {
      return <h3>Hello From Slide Three</h3>;
    };
    
    export default App;
    
    
    .content {
      width: 200px;
      height: 100px;
      overflow: hidden;
      box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.1);
      padding: 1rem;
      position: relative;
    }
    
    h3 {
      position: absolute;
    }
    
    .slide-left-enter {
      opacity: 0;
      transform: translateX(100%);
    }
    .slide-left-enter-active {
      opacity: 1;
      transform: translateX(0%);
      transition: all 2000ms;
    }
    .slide-left-exit {
      opacity: 1;
      transform: translateX(0%);
    }
    .slide-left-exit-active {
      opacity: 0;
      transform: translateX(-100%);
      transition: all 2000ms;
    }
    
    .slide-right-enter {
      opacity: 0;
      transform: translateX(-100%);
    }
    .slide-right-enter-active {
      opacity: 1;
      transform: translateX(0%);
      transition: all 2000ms;
    }
    .slide-right-exit {
      opacity: 1;
      transform: translateX(0%);
    }
    .slide-right-exit-active {
      opacity: 0;
      transform: translateX(100%);
      transition: all 2000ms;
    }
    
    

    the trick is to reverse the translation on every key button