🍣

スクロールした際のフェードアニメ(クラス+observer)

2022/12/02に公開

こちらの記事ではscrollイベントを使ったフェードアニメですが
https://zenn.dev/icchicw/articles/2b08709a2bfc74

scrollイベントはなるべく使いたくないので、オブザーバーに書き換えました
今回こちらで紹介するコードはこれ1個でカバーするためコードがちょっと長いです。
僕の環境下では細かく分けているため約1/4の量です。余裕があれば分けるとなお良いでしょう。

ここでは"js-fadeIn, js-fadeUp, js-fadeLeft, js-fadeRight"クラスが付いている要素に対して、それぞれ"u-fade**"を付与するように設定しています。

CSSクラスについては割愛します。
それぞれお好みで調整してください。

対象や付与するクラスを変えたい場合もお好みで変えてみてください。

main.js
  import { FadeAnime } from './_FadeAnime';
  //fadeAnimation top,bottomはpxまたは%で指定
  //=>new FadeAnime(top, bottom = top, once = false);
  new FadeAnime("-100px");
_FadeAnime.js
'use strict'

export class FadeAnime {
  constructor(top, bottom = top, once = false) {
    this.top = top; //rootMargin top
    this.bottom = bottom; //rootMargin bottom
    this.once = once; //アニメーション継続するか否か
    this.fades = []; //DOM格納用の配列
    this.fadeFlags = []; //各フェード系のDOMが存在するかどうかのフラグ
    this.addCls; //クラス付与用
    this.removeClass; //クラス除去用

    this._makeFadesFunc();
    this._setFlag();
    this._observe();
  }

  //取得したDOMをthis.fadesに格納
  _makeFades(cls) {
    const dom = document.querySelectorAll(cls);
    this.fades.push(dom);
  }
  
  _makeFadesFunc() {
    this._makeFades(".js-fadeIn"); //フェードイン
    this._makeFades(".js-fadeUp"); //フェードアップ
    this._makeFades(".js-fadeLeft"); //フェードレフト
    this._makeFades(".js-fadeRight"); //フェードライト
  }

  _setFlag() {
    //フラグ設定
    this.fades.forEach(fade => {
      fade.length != 0 ? this.fadeFlags.push(true) : this.fadeFlags.push(false);
    });
    //フェード系のDOMが存在しない場合は終了
    if(this.fadeFlags.every(fade => fade === false)) return;
  }

  _addClass(target) {
    const targetClass = target.classList;
    targetClass.forEach(t => {
      if(t.indexOf("js-fade") == 0) this.addCls = `u-${t.split("js-")[1]}`;
    });
    targetClass.add(this.addCls);
  }

  _removeClass(target) {
    const targetClass = target.classList;
    targetClass.forEach(t => {
      if(t.indexOf("js-fade") == 0) this.removeCls = `u-${t.split("js-")[1]}`;
    });
    targetClass.remove(this.removeCls);
  }

  //オブザーバー
  _observe() {
    const _that = this;

    const callback = (entries) => {
      entries.forEach(entry => {
        if(entry.isIntersecting) {
	  //交差したらクラス付与
          _that._addClass(entry.target);
	  //onceがtrueの場合は監視終了
          if(_that.once) {
            observer.unobserve(entry.target);
          }
        } else {
	  //交差しない場合はクラス除去
          _that._removeClass(entry.target);
        }
      });
    }

    const options = {
      root: null, //ビューポートをルート要素とする
      rootMargin: `${this.top} 0px ${this.bottom} 0px`, //判定基準:ビューポート
      threshold: 0, //閾値
      once: `${this.once}`,
    };

    const observer = new IntersectionObserver(callback, options);

    this.fades.forEach(fade => {
      fade.forEach(item => observer.observe(item));
    });
  }
}

Discussion