K's Blog
2021.9.20 JavaScript

脱jQuery|スクロールインアニメーションの実装

サムネイル

スクロールにあわせて要素にアニメーションを加える実装を「jQuery」を使用しないで実装していきます。

それにあわせてスクロールイベントのパフォーマンス向上のためにスクロールイベントの間引きとスクロールジャック(カクツキ)対策も同時に実装していきます。

目次

  1. 実装物
  2. HTML記述
  3. CSS(Scss)記述
  4. JavaScript記述
  5. スクロールイベントの間引き
  6. スクロールジャック(カクツキ)対策
  7. まとめ

実装物

今回実装したものをコードペンで確認してみましょう!

HTML記述

ポイントはふわっとしたアニメーションをあてたい要素に「js-scroll」というクラスを付与することです。

<div class="wrap">
  <div class="item js-scroll">
    <img src="https://i.postimg.cc/65MQ4nt6/rose-1.jpg" alt="">
  </div>

  <!-- 以下ループ -->

</div>

CSS(Scss)記述

今回のふわっとしたアニメーションはcssアニメーションで実装しています。

「opacity」と「transform」でふわっとさせています。

ポイントはアニメーションさせたい要素に発火クラス「-cue」を付与した際にアニメーションがあたるように記述してあるところです。

.wrap{
  padding: 24vw 0;
  width: 100vw;
}
.item{
  width: 80vw;
  height: 80vw;
  min-height: 280px;
  min-width: 280px;
  max-height: 500px;
  max-width: 500px;
  margin: 12vw auto;
  overflow: hidden;

  opacity: 0;
  transform: translate3d(0,80px,0);
  transition: 1s opacity ease, 1s transform ease;
  img{
    height: 100%;
    width: 100%;
    object-fit: cover;
  }

  &.-cue{
    opacity: 1;
    transform: translate3d(0,0,0);
  }
}

JavaScript記述

コード全体

window.addEventListener("DOMContentLoaded", () => {
  const items = document.querySelectorAll(".js-scroll");
  let isScroll = true;
  let fromBtm = 200;

  function onScroll() {
    isScroll = true;

    items.forEach(item => {
      let rect = item.getBoundingClientRect();
      if (rect.top < window.innerHeight - fromBtm) {
        item.classList.add("-cue");
      };
    });
  };
  onScroll();

  window.addEventListener("scroll", e => {
    if (isScroll) {
      requestAnimationFrame(onScroll);
      isScroll = false;
    };
  }, {passive: true});
});

アニメーション対象要素を全取得し、スクロールイベントが走るたびに発火クラス「-cue」を付与しています。

「fromBtm」で画面の下からどれくらいで発火させるかの距離をとっています。

クラス構文

window.addEventListener("DOMContentLoaded", () => {
  class ScrollInAnime {
    constructor(){
      this.items = [...document.querySelectorAll(".js-scroll")];
      this.isScroll = true;
      this.fromBtm = 200;
      console.log(this.items)

      this.onScroll = this.onScroll.bind(this);
      this.init();
    }

    onScroll(){
      this.isScroll = true;

      this.items.forEach(item => {
        let rect = item.getBoundingClientRect();

        if (rect.top < window.innerHeight - this.fromBtm)
          item.classList.add("-cue");
      });
    }

    init(){
      this.onScroll();

      window.addEventListener("resize", e => {
        this.onScroll();
      });

      window.addEventListener("scroll", e => {
        if (this.isScroll) {
          requestAnimationFrame(this.onScroll);
          this.isScroll = false;
        };
      }, {passive: true});
    }
  }
  new ScrollInAnime();
});

スクロールイベントの間引き

スクロールイベントは高い頻出で呼び出させれるイベントです。

そのため無駄に呼び出してしまい、サイトの処理を重くさせたりパフォーマンス低下へとつながっていきます。

そのためフレームレートにあった最適な回数だけ呼び出せるように「requestAnimationFrame」を使用して記述をしていきます。

極端に間引いていく際は「setTimeout」などを使用していくそうです。

window.addEventListener("scroll", e => {
  if (isScroll) {
    requestAnimationFrame( () => {
       isScroll = true;
  
       //・・・ 処理 ・・・

    });
    isScroll = false;
  };
});

このように記述し「requestAnimationFrame」内で処理の記述をすることで、アニメーションフレームと同じ頻度でイベントを呼び出し最適化してくれます。

クロールジャック(カクツキ)対策

スクロールイベントのアニメーションのカクツキは「event.preventDefault()」を探しにいっている間にスクロールアニメーション内の他の処理の待ち時間が発生しているため、カクツキが生じたりします。

window.addEventListener("scroll", e => {
  //・・・ 処理 ・・・
}, {passive: true});

そのため上記のように「passive: true」を最後に記述することで、ブラウザ側が「event.preventDefault()」を使用していないことを非同期に処理してくれるようになるため、スムーズにアニメーションが実行してくれるようになります。

まとめ

最近はリッチなアニメーションを使用するサイトが増えてきていますので、パフォーマンス向上のためにもしっかりと対策をしていきたいところです。

・参考サイト

MDN Web Doc|Element: scroll イベント

MDN Web Doc|EventTarget.addEventListener()

Share on SNS

Web Production Developer
It describes daily experiences and learning.
Square group rather than circle.
For inquiries, please contact DM from SNS.
©︎2021 K's