Vue.js(Nuxt.js) でページ遷移時にアニメーションを付けるTips

## はじめに

**Nuxt.js**や**Next.js**といったJavaScriptフレームワークでは、**SPA(Single Page Application)** を気軽に作成する事ができます。

通常のWebサイトではページ遷移時にリロードがかかるため画面が白くなりますが、SPAではページ遷移時にコンテンツが変わる部分のみを切り替えているので、その特性を利用して**シームレスなアニメーション**を実装する事ができます。

私が以前案件でページ遷移時のアニメーションを実装する際に、[Nuxt.jsの公式ドキュメント](https://nuxtjs.org/ja/docs/get-started/installation)やエンジニアさんのブログなどで実装方法を探していた時に中々ドンピシャな記事が見つからなかったので、備忘録としても残しておきます。


## やりたいこと

こんな感じのページ遷移アニメーション。
![画面収録-2021-12-21-12.54.04.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2319754/5f766aea-652c-5520-7228-860d42d59a7c.gif)



## 環境のセットアップ

任意のディレクトリを用意します。(今回は``/source/``ディレクトリを使います。)

### Nuxt.js インストール

``/source/`` ディレクトリに**Nuxt.js をインストール**します。

```sh
$npm init nuxt-app <project-name>
```

### ローカルサーバー立ち上げ

インストール後のディレクトリで、ローカルサーバーを立ち上げます。

```sh
$npm run dev
```


http://localhost:3000/ を開くと、Nuxt.jsがインストールされていて、デフォルト表示されています。
![nuxt_default.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2319754/c36c7331-de13-489c-3209-45a62aab0681.png)


### ソースファイルの確認
``/source/`` ディレクトリを確認すると、Nuxt.jsのファイル群が生成されています。
![スクリーンショット 2021-12-20 22.39.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2319754/879059ed-b4cb-b7f0-1407-2efac4c73df6.png)

### 使うファイルは2つだけ

下層ページ用に、 ``/pages/index.vue`` をコピーして、``/pages/lower.vue`` を作成します。
今回はページ遷移のアニメーションの実装なので、使うファイルは、2ファイルのみです。

- ``/pages/index.vue`` (トップページ)
- ``/pages/lower.vue`` (下層ページ)



## いざ実装

### トップページ に下層ページへのリンクを配置
```/pages/index.vue
<template>
  <div class="topPage">
    <nuxt-link to="/lower/" class="bnr">to Lower Page</nuxt-link>
  </div>
</template>

<style>
.topPage {
  width: 100vw;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
}

.bnr {
  padding: 15px 30px;
  display: inline-block;
  position: relative;
  color: #fff;
  text-decoration: none;
}
.bnr::before {
  content: "";
  width: 100%;
  height: 100%;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: #000;
  z-index: -1;
}
</style>
```

![スクリーンショット 2021-12-21 14.20.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2319754/2cc3be44-43bf-4d02-b9a3-64dc27476619.png)

``/lower/`` ページへ遷移するためのリンク(``<nuxt-link>``) に.bnr classを付けてスタイリングしています。
ページ遷移時にバナーの黒背景部分を拡大するので、疑似要素``::before`` にしています。

### ``/lower/``ページ をスタイリング

```/pages/lower.vue
<template>
  <div class="lowerPage">
    <h1 class="title">Lower Page</h1>
  </div>
</template>

<style>
.lowerPage {
  width: 100vw;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  background: #000;
}

@keyframes fadeInUp {
  0% {
    opacity: 0;
    transform: translateY(20px);
  }
  100% {
    opacity: 1;
    transform: none;
  }
}

.title {
  font-size: 30px;
  font-weight: bold;
  text-align: center;
  color: #fff;

  animation: fadeInUp 0.8s 0s ease-in-out forwards;
}
</style>

```

![画面収録-2021-12-21-14.25.44.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2319754/e851f16f-24a7-d9e9-7f75-afcc58b74e13.gif)

**「Lower Page」** のタイトルを中央表示した上で、CSSの``@keyframes`` で下からフワっとフェードインするようアニメーションを設定しています。
これで各ページの下準備は完了です。

### ``/pages/index.vue`` に Nuxtのtransitionコンポーネントを設定

```/pages/index.vue
<template>
  <!-- 省略 -->
</template>

<script>
export default {
  transition: "expandFade",
};
</script>
```
Vue.js (Nuxt.js) では**transitionコンポーネント**を設定することで、ページ遷移時にコンポーネントにclassを付けたり、遷移の秒数を設定する事ができます。 
※詳しくは[公式ドキュメント](https://nuxtjs.org/ja/docs/features/transitions/)参照

これでトップページから他ページに遷移する際に、``index.vue`` の.topPage に ``.expandFade-leave-active`` が付加されるようになります。

### CSSにてページ遷移時のアニメーションを設定

``pages/index.vue`` に下記のCSSコードを追加します。

```/pages/index.vue
<style>
/* 省略 */

@keyframes expandFadeOut {
  0% {
  }
  100% {
    width: 100vw;
    height: 100vh;
  }
}

.expandFade-leave-active {
  transition: 0.8s;
}

.expandFade-leave-active .bnr::before {
  animation: expandFadeOut 0.8s 0s ease-in-out forwards;
}
</style>
```

トップページからページ遷移する際に、 .topPageに ``.expandFade-leave-active`` classが付加されるため、その配下にあるリンク(``.bnr``) に``@keyframesアニメーション``がかかる仕組みです。
``@keyframesアニメーション``内では、最初に作ったリンクの黒背景部分(``::before``) のwidth, heightを画面幅まで広げています。

こちらで実装完了となります。


## まとめ

Vue.js(Nuxt.js) では **transitionコンポーネント** を用いる事で、ページ遷移時にコンポーネントにclassを付加する事ができます。
このclassを使う事で、CSSにてアニメーションを自由に実装する事ができます。