steph
steph

Reputation: 53

How can I do a different animation between the pages with Framer Motion and Next.js?

I use Next.js, React, TypeScript and Framer Motion. I don't use react router. I would like to do a transition between pages. I've found how to do the transition from right to the left when I change my page (in _app.tsx). But it's the same transition if I follow the link with the arrow right and with the arrow left. I would like to do a transition from right to left with the arrow right and a transition from left to right with the arrow left.

my _app.tsx:

import '../public/css/style.scss';
import { animate, AnimateSharedLayout, motion } from 'framer-motion';
import { useRouter } from "next/router";
import App from "next/app";
//import Strategie from './strategie'


export default class MyApp extends App { 
 render(){
        const { Component, pageProps } = this.props;
        const { router } = this.props;
        // const { router } = this.props;
        return <>
            <motion.div key={router.route} initial="pageInitial" animate="pageAnimate" exit="pageExit" variants={{
                pageInitial: {
                    opacity: 0,
                    x: "100%", 
                },
                pageAnimate: {
                    opacity: 1,
                    x: 0,
                    transition: {
                        ease: "easeIn",
                        duration: 0.5,
                    }
                },
                pageExit: {
                    // filter: `invert()`,
                    opacity: 0,
                }
            }}> 
                <Component {...pageProps} />
            </motion.div>
        </> 
    }
} 

My page where there are 2 arrows (I have several pages like this) :

import React from "react";
import HomePageBlock, {IdHomePageBlockProps} from '../components/common/home pages/homePage-block';
import ButtonStartProject from "../components/buttons/button-startProject";
import Link from "next/link";
import Sidebar from "../components/common/sidebar/sidebar";



interface IdHomePageSingularityProps {
  title?: string;
}
interface IdHomePageSingularityState{
  homePageInfos: IdHomePageBlockProps[]
}
export default class HomePageSingularity extends React.Component<IdHomePageSingularityProps, IdHomePageSingularityState>{
    constructor(props){
      super(props);
      this.state = {
        homePageInfos: [
          {
            title: <>Révélons <br/> ta singularité</>, 

            subTitle: "notre expertise",

            description: <>Nombreux sont les projets qui émergent aujourd'hui, alors comment se démarquer quand tu as un projet bienveillant sans perdre en efficacité ? <br/> Chez Ben&Jo nous concevons des identités visuelles basés sur une stratégie cohérente. Nous inventerons pour ton projet un univers visuel unique et impactant !</>,
            
            img: { url : "/img/home-pages/singularity.svg"},

            buttonDiscover: <>
              <Link href="/brandingIndex"> 
                <a className="buttonDiscover" >DECOUVRE NOTRE SAVOIR FAIRE !</a>
              </Link></>,

            buttonStartProject: <><ButtonStartProject /></>,

            imgFingersBox: { url : "/img/home-pages/fingersBox.svg"},

            arrowRight: <>
              <Link href="/homePageSucces">
                <img src="/img/home-pages/arrowRight.svg" />
              </Link></>,

            arrowLeft: <>
              <Link href="/homePageStrategie">
                <img src="/img/home-pages/arrowLeft.svg" />
              </Link>
            </>,
          }
        ]
      }
    }
    render(){
      const { homePageInfos } = this.state;
    return <>
        <div className="home__singularity">
          <div><Sidebar /></div>
            { homePageInfos && homePageInfos.map((item, index) => {
                    return <HomePageBlock key={"home__singularity-block_" + index } 
                        {...item} className="home__singularity-block"/>
                })
            }
        </div>
    </>
  }
}

The page where I build the components:

import React from 'react';
import FingersBox from './fingersBox';


export interface IdHomePageBlockProps {
  title?: {};
  subTitle?: string;
  description?: {};
  img?: {url: string, alt?: string};
  buttonDiscover?: {};
  buttonStartProject?: {};
  arrowRight?: {};
  arrowLeft?: {};
  className?: string;
  imgFingersBox?: {url: string, alt?: string};

}
export interface IdHomePageBlockState{
  
}
export default class HomePageBlock extends React.Component<IdHomePageBlockProps, IdHomePageBlockState>{
  render(){
    const { title, subTitle, description, img, buttonDiscover, buttonStartProject, arrowRight, arrowLeft, className } = this.props;
    return <>
    <div className={className + ' homePage-block'}>
      <header>
        <div className="homePage-block__imageFingers-box"><FingersBox /></div>
        <div className="homePage-block__subTitle">{subTitle}</div>
        <div className="homePage-block__buttonStartProject">{buttonStartProject}</div>
      </header>
      <body>
        <main>
          <div className="homePage-block__title">{title}</div>
          <div className="homePage-block__description">{description}</div>
          <div className="homePage-block__buttonDiscover">{buttonDiscover}</div>
        </main>        
        <aside>
          <div className="homePage-block__image-box">
            <img src={img.url} alt={img.alt} />
          </div>
        </aside> 
      </body>       
      <footer>
        <div className="homePage-block__arrowLeft">{arrowLeft}</div>
        <div className="homePage-block__arrowRight">{arrowRight}</div>
      </footer>
    </div>
    </>
  }
}

As you can see, there is no button, just links.

Upvotes: 1

Views: 2078

Answers (1)

Deadfish
Deadfish

Reputation: 2057

You can use dynamic variants in framer motion to achieve this.

export default class MyApp extends App { 

constructor(props) {
    super(props)
    this.state = { direction: 'left' }
}

changeDirection(direction: string) {
    this.setState({
      direction
    });
  }

render(){
        const { direction } = this.state
        const { Component, pageProps } = this.props;
        const { router } = this.props;
        // const { router } = this.props;
        return <>
            <motion.div key={router.route} custom={direction} initial="pageInitial" animate="pageAnimate" exit="pageExit" variants={{
                pageInitial: direction => ({
                    opacity: 0,
                    x: direction === 'left' ? "100%" : "-100%", 
                }),
                pageAnimate: {
                    opacity: 1,
                    x: 0,
                    transition: {
                        ease: "easeIn",
                        duration: 0.5,
                    }
                },
                pageExit: {
                    // filter: `invert()`,
                    opacity: 0,
                }
            }}> 
                <Component changeDirection={this.changeDirection} {...pageProps} />
            </motion.div>
        </> 
    }
} 

In the actual Page Component, you might need to figure out a way to change the direction as required before the navigation is initiated.

import React from "react";
import { withRouter } from 'next/router'
import HomePageBlock, {IdHomePageBlockProps} from '../components/common/home pages/homePage-block';
import ButtonStartProject from "../components/buttons/button-startProject";
import Link from "next/link";
import Sidebar from "../components/common/sidebar/sidebar";

interface IdHomePageSingularityProps {
  title?: string;
}
interface IdHomePageSingularityState{
  homePageInfos: IdHomePageBlockProps[]
}
class HomePageSingularity extends React.Component<IdHomePageSingularityProps, IdHomePageSingularityState>{
    constructor(props){
      super(props);
      this.state = {
        homePageInfos: [
          {
            title: <>Révélons <br/> ta singularité</>, 

            subTitle: "notre expertise",

            description: <>Nombreux sont les projets qui émergent aujourd'hui, alors comment se démarquer quand tu as un projet bienveillant sans perdre en efficacité ? <br/> Chez Ben&Jo nous concevons des identités visuelles basés sur une stratégie cohérente. Nous inventerons pour ton projet un univers visuel unique et impactant !</>,
            
            img: { url : "/img/home-pages/singularity.svg"},

            buttonDiscover: <>
              <Link href="/brandingIndex"> 
                <a className="buttonDiscover" >DECOUVRE NOTRE SAVOIR FAIRE !</a>
              </Link></>,

            buttonStartProject: <><ButtonStartProject /></>,

            imgFingersBox: { url : "/img/home-pages/fingersBox.svg"},

            arrowRight: <>
              <a onClick={this.handleRightClick} href="/homePageSucces">
                <img src="/img/home-pages/arrowRight.svg" />
              </a></>,

            arrowLeft: <>
              <a onClick={this.handleLeftClick} href="/homePageStrategie">
                <img src="/img/home-pages/arrowLeft.svg" />
              </a>
            </>,
          }
        ]
      }
    }
    const handleRightClick = (e) => {
      e.preventDefault()
      this.changeDirection('right')
      this.props.router.push('/homePageSucces')
    }
const handleLeftClick = (e) => {
      e.preventDefault()
      this.changeDirection('left')
      this.props.router.push('/homePageStrategie')
    }
    render(){
      const { homePageInfos } = this.state;
    return <>
        <div className="home__singularity">
          <div><Sidebar /></div>
            { homePageInfos && homePageInfos.map((item, index) => {
                    return <HomePageBlock key={"home__singularity-block_" + index } 
                        {...item} className="home__singularity-block"/>
                })
            }
        </div>
    </>
  }
}
export default withRouter(HomePageSingularity)

Upvotes: 2

Related Questions