[Vue.js]スクロールでコンテンツがフェードインする実装

# はじめに
LPやコーポレートサイトでよく、スクロールするとコンテンツがフワッとフェードインするサイトが見られます。
本記事は、自分がNuxt.jsを使ってこの機能を実装した時に、**ライブラリ、jQuery未使用**で実装した内容を記事にします。
完成イメージを[CodePen](https://codepen.io/)に載せているのでご参考にしてください。

<p class="codepen" data-height="265" data-theme-id="light" data-default-tab="html,result" data-user="RyoTa0222" data-slug-hash="KKVPgzB" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="scroll-fade-in">
  <span>See the Pen <a href="https://codepen.io/RyoTa0222/pen/KKVPgzB">
  scroll-fade-in</a> by Matsumoto (<a href="https://codepen.io/RyoTa0222">@RyoTa0222</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

# 目次
1. コンポーネントの作成
2. v-ifとv-showの違い
3. アニメーションを適用する
4. 動作確認
5. 最後に


# 1. コンポーネントの作成
Vue.jsで実装するため、機能が共通している部分をコンポーネントにします。今回では、「スクロールしていくとコンテンツがフェードインする」という部分が共通の機能となるため、この部分をコンポーネントにします。

```vue
<template>
    <div>
      <slot v-show="visible"></slot>
    </div>
</template>

<script>
  export default {
    data() {
      return {
        visible: false
      };
    },
    created() {
      window.addEventListener("scroll", this.handleScroll);
    },
    destroyed() {
      window.removeEventListener("scroll", this.handleScroll);
    },
    methods: {
      handleScroll() {
        if (!this.visible) {
          var top = this.$el.getBoundingClientRect().top;
          this.visible = top < window.innerHeight + 100;
        }
      }
    }
  }
</script>
```
テンプレートの部分では、`slot`でコンテンツの置換を行っています。変数の`visible`はデフォルトで`false`となっており、非表示の状態になっています、このコンポーネントでは、スクロールイベントを設定し、デフォルトで非表示の要素が一定の高さにくると`visible`が`true`になり、表示されます。
`this.$el.getBoundingClientRect().top;`では、ブラウザの表示領域の左上を(0, 0)として、そこから要素の上端までの相対位置の値が返ってきます。なので、`this.visible = top < window.innerHeight + 100;`では、ブラウザのウインドウの高さに100pxを足した値よりtopの値が小さくなった時に、`this.visible`に`true`が代入されます。

次に、描画するかどうかを`v-if`と`v-show`のどちらで切替を行うかについてお話します。

# 2. v-ifとv-showの違い
`v-if`はDOMへの描画の切替を行います。そのため、DeveloperToolsを用いるとその要素が描画されていないことがわかります。
一方で、`v-show`は、画面への表示、非表示の切替がCSSで行われます。つまりDOM上では常に描画された状態であり、例えば`v-show="false"`は、CSSで`display: none;`が与えられて非表示になっています。

今回のケースでどちらを用いるのが最適かというと、いろいろなサイトを見てもらうとわかるのですが、ページの長さがコンテンツの表示、非表示に関係なく同様となっています。つまり、DOM上には常に描画された形であり、**`v-show`によって切り替えるのが正しいです。**

# 3. アニメーションを適用する
Vue.jsでアニメーションといえば、トランジションをまず頭に思い浮かべます[(トランジションについて)](https://jp.vuejs.org/v2/guide/transitions.html)。Vue.jsのドキュメントにも以下のように書いてあります。

> Vue は、transition ラッパーコンポーネントを提供しています。このコンポーネントは、次のコンテキストにある要素やコンポーネントに entering/leaving トランジションを追加することを可能にします:

> - 条件付きの描画 (v-if を使用)
> - 条件付きの表示 (v-show を利用)
> - 動的コンポーネント
> - コンポーネントルートノード (Component root nodes)

ただ、ここまで説明しておいて、今回はトランジションを使っておらず、CSSのanimationを使ってフェードインを実装しています:upside_down:
理由としては、自分がなかなかトランジションを使いこなせていないからです、、、そのうちトランジションマスターになってこの記事が更新されると信じています笑

```vue
<template>
  <div :class="{fadeIn: visible}">
    <slot v-show="visible"></slot>
  </div>
</template>

<style>
.fadeIn {
  animation: fadeIn 2s;
}
@keyframes fadeIn {
  0% {
    opacity: 0;
    transform: translateY(100px);
  }
  100% {
    opacity: 1;
    transform: translateY(0px);
  }
}
```
ここでは、templateのdiv要素に対して、visibleがtrueとなった場合に動的に`fadeIn`というクラス名が付与されています。
そして、CSSでfadeInという名前の`@keyframes`でアニメーションを作っています。

# 4. 動作確認
今回はコンポーネントの名前を`FadeInComponent`としています。
このコンポーネントを呼び出す時は、`<fade-in-component>`タグの子要素に表示させたい中身を記述するだけです！

```vue
<fade-in-component>
   <section>
       <div class="wrapper">
          <h1>Title00</h1>
          <h2>subtitle00</h2>
          <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
          <h2>subtitle01</h2>
          <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. </p>
          <div class="d-flex justify-space-around my-4">
          　　<v-btn depressed color="info">button</v-btn>
             <v-btn outlined color="info">button</v-btn>
          </div>
       </div>
   </section>
</fade-in-component>
```
動作確認の方は、冒頭のCodePenの方で確認してみてください！また、`animation: fadeIn 2s;`の秒数や`this.visible = top < window.innerHeight + 100;`の部分を変えたりして、好みに調整してみてください！

# 5. 最後に
今回実装したものは一つの方法でしかありません。ライブラリやjQueryに依存せず、他にももっといい方法があるかもしれないので、発見出来次第、追記していきたいと思っています！