【Nuxt.js】スクロールアニメーション実践編：思わず最後までスクロール

![picture_pc_ed98807d7df90b1f046ef8e3e3a0d1c4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469755/f1350af4-e38f-7858-8825-d5a2f12a1ccb.gif)

# 前置き

スクロールエフェクトのアニメーション
青い背景が左からニョキっと出てきます👀
動きがあるとスクロールしたくなりますよね🎵

今回はシンプルですが、
背景やタイトルを動かしたり
アイテムを浮き出させたり、使い方は様々です🌟

# 構成

大まかにこんな感じです🍒
・mouted()でDOMから
　Y座標を取ってきてdata更新
・Y座標を1増やし(下げる)watchを反応させる
　対応するsection idにanimationクラスを付与

# 完成コード

今回はいつものようにStep順ではなく
構成で全体感を説明しているため、
部分的に分けて解説していきます！

・section id="second"
　スクロールしていき
　この領域に入ったらアニメーション開始💃✨

```index.vue
<template>
 <div class="page">
   <p>スクロールしてみよう</p>
   <section id="first">
     <p>もう少し</p>
   </section>
   <section id="second">
     <div class="bg">
       <p>ここが動く</p>
       <div />
     </div>
   </section>
   <section id="third">
     <p>伸びたね</p>
   </section>
 </div>
</template>

<script>
import Vue from 'vue'

export default {
 data () {
   return {
     positionY: 0,
     positions: {
       second: 0,
     },
   }
 },
 watch: {
   positionY(to, from) {
     const scrollOffset = to + 100
     let el

     if (this.positions.second <= scrollOffset) {
       el = document.getElementById('second')
       el.setAttribute('class', 'animation')
     }
   },
 },
 mounted() {
   window.addEventListener('scroll', this.checkScroll)

   this.positions = {
     second: document.getElementById('second').getBoundingClientRect().top,
   }

   this.positionY++
 },
 methods: {
   checkScroll() {
     this.positionY = window.scrollY ? window.scrollY : window.pageYOffset
   },
 }
}
</script>

<style lang="scss" scoped>
 #second {
 > .bg {
   width: 100%;
   height: 100%;

   div {
     width: 25%;
     height: 200px;
     background: linear-gradient(269.76deg, #1F3345 0%, #333A56 100.52%);
     transition: all 3s cubic-bezier(0.215, 0.61, 0.355, 1);
     display: block;
   }
 }
 &.animation {
   > .bg {
     div {
       width: 100% !important;
       display: block;
     }
   }
 }
}
</style>
```

# template

section idで区切ります。
watchでスクロールによる
idへのクラスを付与するためです。


# data

・positionY
　Y軸を取得しwatchを反応させるため
・positions
　該当idの一番上の位置を代入させるため

```index.vue
<script>
import Vue from 'vue'

export default {
 data () {
   return {
     positionY: 0,
     positions: {
       second: 0,
     },
   }
 },
}
</script>
```

# mounted()

解説はインラインのコメントにて。
基本的にスクロールや座標の指定は
DOMの表示位置から取るため
大体のことはここに書きます✍️

```index.vue
<script>
import Vue from 'vue'

export default {
 mounted() {
   // スクロールイベントを追加
   window.addEventListener('scroll', this.checkScroll)

   this.positions = {
     // section id="second"のtopのY座標をdataのsecondに代入
     second: document.getElementById('second').getBoundingClientRect().top,
   }

   // watchを反応させるために1だけ下げる
   this.positionY++
 },
}
</script>
```

# methods

解説はインラインのコメントにて。
先ほどのmountedで実行している関数です。
ページ内容に関係なく今いる位置の一番上の
Y軸の位置を取れば良いだけなのでDOM関係なし！

```index.vue
<script>
import Vue from 'vue'

export default {
  methods: {
   checkScroll() {
     // window.scrollYでY軸取得できる
     // this.positionY = window.scrollYのみでもOK
　　　// IE対応のために三項演算　式1? 式2: 式3
     this.positionY = window.scrollY ? window.scrollY : window.pageYOffset
   },
  }
}
</script>
```

# watch

解説はインラインのコメントにて。

```index.vue
<script>
import Vue from 'vue'

export default {
  watch: {
   // dataのpositionY
   // fromが変化する前の値, toが変化後の値
   positionY(to, from) {
     // sectionがy=0の1番上に来た時点ではなく、
　　　// + 100の位置に来た時点で反応させたい
     const scrollOffset = to + 100
     let el

     // secondのtopの位置が50、それ以下(上)になったら
     if (this.positions.second <= scrollOffset) {
       // id="second"に
       el = document.getElementById('second')
       // animationというクラスを追加
       el.setAttribute('class', 'animation')
     }
   },
 },
}
</script>
```

スクロールにより
途中でanimationクラスが
追加されているのが分かりますね👀

# css

それぞれを書けばOK！
・通常時
・animationクラス付与時

変化が分かりやすいように
transitionは3秒と長めにとりました。

```index.vue
<style lang="scss" scoped>
 #second {
 > .bg {
   width: 100%;
   height: 100%;

   div {
     width: 25%;
     height: 200px;
     background: linear-gradient(269.76deg, #1F3345 0%, #333A56 100.52%);
     transition: all 3s cubic-bezier(0.215, 0.61, 0.355, 1);
   }
 }
 &.animation {
   > .bg {
     div {
       width: 100% !important;
     }
   }
 }
}
</style>
```



🌟**記事の量が増えてきました。
　知りたい内容があれば、
　aLizとそのキーワードでググってみてください♪**
　ヒットしない場合、
　こんな記事が欲しい！とコメントいただければ
　記事にするかもしれません🎈

記事が公開したときにわかる様に、
note・Twitterフォローをお願いします😀