ページ遷移


滑らかでクールなページトランジションは、我々はすべての閲覧中に閲覧するのが大好きなものですDribbble . 私は常に魅了され、私は自分のサイトのためにそれを行う方法を自分で尋ねた.
一度、私はそれを構築したサイトで達成することができたNext.js 図書館を利用することnext-page-transitions . CSSで私が欲しいトランジションを作成できました.しかし、私は問題を打ちました.
それはCSSクラスを通して作られたので、非常に制限されていて、柔軟性がありませんでした.私はクラスの多くを持つことなく、すべてのページにカスタム体験を作成することができなかったと再レンダリングに対処する必要があります.ありがたいことに、フレマーモーションのアニメーションの存在APIは、簡単にこれらの問題について心配することなく、任意の反応フレームワークでなめらかなカスタムページ遷移を作成することができます.

アニマルプレゼンス
私の前では<AnimatePresence/> コンポーネント.それはexit 彼らは反応のレンダリングツリーから削除されているすべての子供からプロップアニメーション.基本的には、コンポーネントがアンマウントされたときに検出し、このプロセスをアニメーション化します.
最近、フレーマー運動という小道具が導入されたexitBeforeEnter . それがtrueにセットされるならば、それは一度に1つの構成要素だけを与えます.新しいコンポーネントがレンダリングされる前に、既存のコンポーネントがアニメーションを終了するのを待ちます.これは、コンポーネントやページのみを一度にレンダリングされることを保証することができますので、ページ遷移を処理するために最適です.

小さな例
私たちが学んだことをテストしましょう<AnimatePresence/> . 第一に、私たちはexitBeforeEnter 簡単に移行することでプロップする方法を見ることができます.
このウェブサイトは電子商取引の模倣になるだろう.それは2つのページがあります:お店とお問い合わせください.彼らは非常にシンプルなレイアウトがあります.このように:


我々の第一歩は、我々のページを中で包むことです<AnimatePresence/> . 我々がラップするところで、我々のルータがページを描いているところに依存します.念頭に置いて、各子供たちはユニークな必要があることを忘れないでくださいkey propので、ツリー内の存在を追跡することができます.
次に.JS私たちは_app.js ファイルとラップ<Component> with <AnimatePresence/> .
// pages/_app.js

import { AnimatePresence } from "framer-motion";
import "../styles/index.css";

function MyApp({ Component, pageProps, router }) {
  return (
    <AnimatePresence>
      <Component key={router.route} {...pageProps} />
    </AnimatePresence>
  );
}

export default MyApp;
作成反応アプリのために、我々はどこに我々のルータがページをレンダリングしているそれを使用します.
import React from "react";
import { Switch, Route, useLocation, useHistory } from "react-router-dom";
import { AnimatePresence } from "framer-motion";

const App = () => {
  const location = useLocation();

  return (
    <AnimatePresence>
      <Switch location={location} key={location.pathname}>
        <Route path="/contact" component={IndexPage} />
        <Route path="/contact" component={ContactPage} />
      </Switch>
    </AnimatePresence>
  );
};

💡 Check out the website's code for each framework in this GitHub repository.


今、我々はすべてのページを包んでいる<AnimationPresence> , ルートを変更しようとすると、現在のコンポーネントがアンマウントしないことに気づくでしょう.

これは、フレームの動きは、各ページの終了アニメーションを探しているため、これは発生しますが、我々は定義されていないため、それは見つかりませんmotion コンポーネント.
いくつかの単純なフェードアウトアニメーションを各ページに追加しましょう.このように:
import { motion } from "framer-motion"

<motion.div exit={{ opacity: 0 }}>
    ... content
</motion.div> 
そして今、コンポーネントはアンマウントすることができます!

あなたが密接な注意を払うならば、我々の接触形が消える前に、インデックスページは下に現れます.そして、気晴らしをつくって、我々のアニメーションの流動性を破滅させます.もし我々がインデックスページにマウントアニメーションを持っているなら、これは本当に悪いでしょう.
ここがどこですexitBeforeEnter プロップは便利です.新しいコンポーネントを読み込む前に、コンポーネントがアンマウントされることを保証します.我々がpropを加えるならば<AnimatePresence/> , あなたはそれがもはや問題ではないことに気づきます、そして、我々の移行は滑らかで、必要に応じて働いています.
<AnimatePresence exitBeforeEnter/>

これはframerモーションでトランジションを作成するために必要なすべてです.それが我々が今できることになるとき、空は限界です!

ドリブルからの美しい移行
あなたはドリブルで見られるような素晴らしい移行を作成したいことがありますか?私は常に持っている.ありがたいことに、フレマーの動きは、これらを再簡単に作成できます.このデザインを見てくださいFranchesco Zagami :

この素晴らしい移行を再作成しようとしましょう.
遷移プロトタイプを翻訳するとき、アニメーションのeasingsと詳細がわかることができるように、それはオリジナルのファイルを持つのが最もよいでしょう.しかし、ドリブルデザインを取っているので、その値を推定することで再作成します.

初期遷移
我々が最初に見る要素の1つは、画面の端に向かって動く黒い背景です.これは、Framerの抽象化のために再作成するのが本当に簡単です.
まず、すべての初期遷移ロジックを格納するコンポーネントを作成します.
const InitialTransition = () => {};
第二に、画面のサイズを持つブラック広場を追加します.
const blackBox = {
  initial: {
    height: "100vh",    
  },
};

const InitialTransition = () => {
  return (
    <div className="absolute inset-0 flex items-center justify-center">
      <motion.div
        className="relative z-50 w-full bg-black"
        initial="initial"
        animate="animate"
          variants={blackBox}
      />      
    </div>
  );
};
代わりにmotion 小道具、我々はより多くの要素を処理する必要がありますさらにダウン以来、変種を使用します.

💡 If you want to learn how to use Framer Motion variants, you can check out my !


これまで、我々は我々のスクリーンの真ん中に黒い正方形を持ちます.私たちはbottom and height プロパティを下に移動を作成します.The bottom プロパティは、それが底に向かって崩壊する.
const blackBox = {
  initial: {
    height: "100vh",
    bottom: 0,
  },
  animate: {
    height: 0,    
  },
};

const InitialTransition = () => {
  return (
    <div className="absolute inset-0 flex items-center justify-center">
      <motion.div
        className="relative z-50 w-full bg-black"
        initial="initial"
        animate="animate"
          variants={blackBox}
      />      
    </div>
  );
};
これは我々が今持っているものです.

あなたが我々の参照にこれを比較するならば、あなたはアニメーションが非常に速く起こるのに気がつきます、そして、十分に流動的でありません.これを修正することができますtransition プロパティ.修正しますduration 私たちのアニメーションを遅くするease それを滑らかにする.
const blackBox = {
  initial: {
    height: "100vh",
    bottom: 0,
  },
  animate: {
    height: 0,
    transition: {
      duration: 1.5,
      ease: [0.87, 0, 0.13, 1],
    },
  },
};
もっとよく似ています.

今、我々はテキストを再作成する必要があります.まあ、何か違うことをします.我々のテキストがNavbarの中央に位置していないので、我々はちょうどそれをフェードアウトします.
私たちが近い外観を取るならば、それはマスクに類似した生き生きした層を持っているので、テキストは黒い正方形より少し難しいです.この効果を達成する方法はSVG要素、特に<text/> and <pattern/> . 次のようになります.
<motion.div
  className="absolute z-50 flex items-center justify-center w-full bg-black"
  initial="initial"
  animate="animate"
  variants={blackBox}
>
    <motion.svg className="absolute z-50 flex">
      <pattern
        id="pattern"
        patternUnits="userSpaceOnUse"
        width={750}
        height={800}
        className="text-white"
      >
        <rect className="w-full h-full fill-current" />
        <motion.rect className="w-full h-full text-gray-600 fill-current" />
      </pattern>
      <text
        className="text-4xl font-bold"
        text-anchor="middle"
        x="50%"
        y="50%"
        style={{ fill: "url(#pattern)" }}
      >
        tailstore
      </text>
    </svg>
</motion.svg>
これは、カスタムテキストを<pattern/> . それには2つある<rect/> . テキストの色と他のアニメーションのための1つmotion 要素.基本的に、後者は隠れるし、白い色を残します.
これをアニメーション化しましょう.
まず、新しい紹介しましょうtransition 財産when . 要素がアニメーションを実行するときには' time 'を定義します.我々は、すべての子供がレンダリングされたときに私たちのブラックボックスが消えてほしいafterChildren :
const blackBox = {
  initial: {
    height: "100vh",
    bottom: 0,
  },
  animate: {
    height: 0,
    transition: {
      when: "afterChildren",
      duration: 1.5,
      ease: [0.87, 0, 0.13, 1],
    },
  },
};
現在、我々のテキストがレンダリングを終えるとき、我々のブラックボックスはそのアニメーションをします.
第二に、我々は<svg/> . 以下はその変形です.
const textContainer = {
  initial: {
    opacity: 1,
  },
  animate: {
    opacity: 0,
    transition: {
      duration: 0.25,
      when: "afterChildren",
    },
  },
};

<motion.svg variants={textContainer} className="absolute z-50 flex"></motion.svg>
最後に<rect/> :
const text = {
  initial: {
    y: 40,
  },
  animate: {
    y: 80,
    transition: {
      duration: 1.5,
      ease: [0.87, 0, 0.13, 1],
    },
  },
};

<motion.rect
  variants={text}
  className="w-full h-full text-gray-600 fill-current"
/>

💡 You may be asking yourself where do I get most of these animation values. All of them except the ease were fine tweaked through estimation. For easing, I used this cheat sheet, specifically the easeInOutExpo values.


これらすべてをフックしてください.

すごい!それは我々のデザインに非常に近いです.
私たちの画面は、我々の移行を示してビジー状態になっているにもかかわらず、まだスクロールすることができることに気づいたかもしれません.幸運にもこれは本当に簡単に修正することです.我々はちょうど適用する必要がありますoverflow: hidden 我々にbody それが行われるとき、それがアニメーション化されて、それを削除するとき.
ありがたいことに.motion コンポーネントには、この正確な状況のイベントリスナーがあります.onAnimationStart , and onAnimationComplete . 前者はanimate 開始し、後者が終了する.
我々のInitialTransition 次を追加します.
<motion.div
  className="absolute z-50 flex items-center justify-center w-full bg-black"
  initial="initial"
  animate="animate"
  variants={blackBox}
  onAnimationStart={() => document.body.classList.add("overflow-hidden")}
  onAnimationComplete={() =>
    document.body.classList.remove("overflow-hidden")
  }
> 
</motion.div>

内容のアニメーション化
すべてが残っている私たちのコンテンツのための滑らかなアニメーションを作成することです.我々は、それが我々のサイトに非常によく合わないので、デザインと同じアニメーションをコピーしません.我々がすることは、子供に対するダウン効果の驚異的なフェードです.変種を作りましょう
const content = {
  animate: {
    transition: { staggerChildren: 0.1, delayChildren: 2.8 },
  },
};

const title = {
  initial: { y: -20, opacity: 0 },
  animate: {
    y: 0,
    opacity: 1,
    transition: {
      duration: 0.7,
      ease: [0.6, -0.05, 0.01, 0.99],
    },
  },
};

const products = {
  initial: { y: -20, opacity: 0 },
  animate: {
    y: 0,
    opacity: 1,
    transition: {
      duration: 0.7,
      ease: [0.6, -0.05, 0.01, 0.99],
    },
  },
};

export default function IndexPage() {
  return (
    <motion.section exit={{ opacity: 0 }}>
      <InitialTransition />

      <motion.div
        initial="initial"
        animate="animate"
        variants={content}
        className="space-y-12"
      >
        <motion.h1 variants={title} className="text-6xl font-black text-center">
          Welcome to tailstore!
        </motion.h1>

        <motion.section variants={products} className="text-gray-700 body-font">
        </motion.section>
      </motion.div>
    </motion.section>
  );
}
他のプロパティのほとんどに精通しているでしょうdelayChildren . これは、繁殖アニメーションのすべての子供に遅延を適用します.言い換えれば、一定時間後に子供たちが表示されます.
これを除いて、我々はちょうど要素をフェードダウンし、0.7秒の期間を追加し、緩和をスムーズにします.結果を以下に示します.

我々の接触ページのために同じことをしましょう:
const content = {
  animate: {
    transition: { staggerChildren: 0.1 },
  },
};

const title = {
  initial: { y: -20, opacity: 0 },
  animate: {
    y: 0,
    opacity: 1,
    transition: {
      duration: 0.7,
      ease: [0.6, -0.05, 0.01, 0.99],
    },
  },
};

const inputs = {
  initial: { y: -20, opacity: 0 },
  animate: {
    y: 0,
    opacity: 1,
    transition: {
      duration: 0.7,
      ease: [0.6, -0.05, 0.01, 0.99],
    },
  },
};

<motion.section
  exit={{ opacity: 0 }}
  class="text-gray-700 body-font relative"
>
  <motion.div variants={content} animate="animate" initial="initial" class="container px-5 py-24 mx-auto">
    <motion.div variants={title} class="flex flex-col text-center w-full mb-12">     
    </motion.div>
    <motion.div variants={inputs} class="lg:w-1/2 md:w-2/3 mx-auto">        
    </motion.div>
  </motion.div>
</motion.section>


UX改善
連絡先と店間の移行は、それが再び最初の遷移を再生するので、長い間かかります.これを行うたびにユーザーを困らせる.
我々は、ユーザーがロードされた最初のページであればアニメーションを再生するだけでこの問題を解決することができます.これを達成するために、ルート変更をグローバルに聞いて、最初のレンダリングかどうかを判断します.それがあるならば、我々は最初の移行を示します;そうでなければスキップし、子プロセスの遅延を取り除く.
次に.JSは経路変更を検出するrouteChangeStart イベントオン_app.js .

💡 Solutions will vary between frameworks. For the sake of keeping this blog post as simple as possible, I will elaborate on Next.js implementation. However, the repository will have solutions in their respective framework.


On _app.js :
function MyApp({ Component, pageProps, router }) {
  const [isFirstMount, setIsFirstMount] = React.useState(true);

  React.useEffect(() => {
    const handleRouteChange = () => {
      isFirstMount && setIsFirstMount(false);
    };

    router.events.on("routeChangeStart", handleRouteChange);

    // If the component is unmounted, unsubscribe
    // from the event with the `off` method:
    return () => {
      router.events.off("routeChangeStart", handleRouteChange);
    };
  }, []);

  return (
    <Layout>
      <AnimatePresence exitBeforeEnter>
        <Component
          isFirstMount={isFirstMount}
          key={router.route}
          {...pageProps}
        />
      </AnimatePresence>
    </Layout>
  );
}
ユーザーが最初のルート変更を行うときのみ更新されます.そして、現在のレンダリングされたページへのプロップとしてこの変数を渡します.
我々のindex.js :
const content = (isFirstMount) => ({
  animate: {
    transition: { staggerChildren: 0.1, delayChildren: isFirstMount ? 2.8 : 0 },
  },
});

// ...

export default function IndexPage({ isFirstMount }) {
  return (
    <motion.section exit={{ opacity: 0 }}>
      {isFirstMount && <InitialTransition />}

      <motion.div
        initial="initial"
        animate="animate"
        variants={content(isFirstMount)}
        className="space-y-12"
      >
        <motion.h1 variants={title} className="text-6xl font-black text-center">
        </motion.h1>

        <motion.section variants={products} className="text-gray-700 body-font">        
        </motion.section>
      </motion.div>
    </motion.section>
  );
}
それだ!私たちのページには驚くべきトランジションがあり、ユーザーは何度も同じアニメーションを再生することに悩まされません.

結論
なめらかなページ遷移は、ものすごいウェブ経験を成し遂げることに非常に重要です.CSSを使用すると、多くのクラスと独立性の欠如に対処するので、維持するのは難しいことができます.ありがたいことに、フレマーの動きは、アニメーションの存在と、この問題を解決します.結合したexitBeforeEnter , これは、開発者は素晴らしいページの遷移を作成することができます.これは非常に柔軟で強力なコードのいくつかの行を介して、我々は複雑なアニメーションを模倣することができますドリブルで発見.
あなたが将来の雇い主またはクライアントに感銘を与えることができるように、このポストはあなたにものすごいページ遷移をつくるように促します.
より最新のウェブ開発内容のために、私に続いてください!読書ありがとう!😎
私はニュースレターを知っていましたか?📬
あなたが私が新しいブログ柱を公表して、ウェブ開発において前に滞在するものすごい毎週の資源を受け取るとき、あなたが通知されたいならばhttps://jfelix.info/newsletter .