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


前置き

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

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

構成

大まかにこんな感じです🍒
・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フォローをお願いします😀