Web production note

 【更新日 :

【脱Jquery】javascriptでフェード系アニメーション(fadeIn、fadeOut、fadeToggle)を実装する

Category:
JavaScript

Vanilla JSでフェード系アニメーション(fadeIn、fadeOut、fadeToggle)を実装したサンプルです。
イージング編集と、コールバック関数に対応させました。
後から作成したWeb Animations API 版の方がより最適化してあります。

Web Animations API 版

codepen

See the Pen fadeIn fadeOut fadeToggle Vanilla JS (Web Animations API) by web_nak (@web_walking_nak) on CodePen.

JavaScript

/**
 * Web Animations APIによるfadeIn・fadeOut・fadeToggleアニメーション処理関数
 * @type {Object}
 * @param {HTMLElement} target ターゲット設定(必須)
 * @param {string} animeType アニメーションの種類 'fadeIn' or 'fadeOut' or 'fadeToggle'(任意 デフォルト:'fadeToggle')
 * @param {number} duration アニメーション時間 ミリ秒(任意 デフォルト:400)
 * @param {string} easing 'Web Animations API'で設定できるイージング(任意 デフォルト:ease)
 * @param {string} displayStyle 'fadeIn'表示後に付与されるcss displayの値(任意 デフォルト:block)
 * @param {function} callBack アニメーション後に呼び出すコールバック関数(任意 デフォルト:null)
 */
const fadeAnime = async setOptions => {
  'use strict';

  const typeFadeToggle = 'fadeToggle';
  const typeFadeIn = 'fadeIn';
  const typeFadeOut = 'fadeOut';

  //デフォルト設定
  const defaultOptions = {
    target: false,
    animeType: typeFadeToggle,
    duration: 400,
    easing: 'ease',
    displayStyle: 'block',
    callBack: null,
  }

  //設定をマージ
  const options = Object.assign({}, defaultOptions, setOptions);

  //要素が存在しない場合は処理を終了
  const target = options.target;
  if(!target) {
    return;
  }

  //fadeToggle分岐
  let animeType = options.animeType;
  const styles = getComputedStyle(target);
  const textNone = 'none';
  const isDisplayNone = styles.display === textNone;
  if (animeType === typeFadeToggle) {
    animeType = isDisplayNone ? typeFadeIn : typeFadeOut;
  }

  const busyClass = 'is-fade-busy';
  const targetClassList = target.classList;

  //既に表示されている or 実行中だった場合は処理しない
  const isFadeIn = animeType === typeFadeIn;
  const isFadeOut = animeType === typeFadeOut;
  const isBusy = targetClassList.contains(busyClass);
  if (
    //fadeIn 既に表示されている or 実行中だった場合は処理しない
    (isFadeIn && (!isDisplayNone || isBusy))
    //fadeOut 既に非表示 or 実行中だった場合は処理しない
    || (isFadeOut && (isDisplayNone || isBusy))
    //有効なキーワードではない場合も処理しない
    || (!isFadeIn && !isFadeOut)
  ) {
    return false;
  }
  //重複処理対策class追加
  targetClassList.add(busyClass);


  //フェードアニメーション
  const targetStyle = target.style;
  const displayStyle = options.displayStyle;

  let opacityAnimeValue;
  if(isFadeOut) {
    //opacity 1 → 0 へアニメーション
    targetStyle.opacity = 1;
    opacityAnimeValue = 0;

  } else {
    //opacity 0 → 1 へアニメーション
    targetStyle.display = displayStyle;
    targetStyle.opacity = 0;
    opacityAnimeValue = 1;
  }
  await target.animate(
    {
      opacity: opacityAnimeValue
    },
    {
      duration: options.duration,
      easing: options.easing
    }
  ).finished;

  //アニメーション終了処理
  //opacity削除
  targetStyle.opacity = '';

  //実行中class削除
  targetClassList.remove(busyClass);

  if(isFadeOut) {
    //要素を非表示
    targetStyle.display = textNone;
  }

  //コールバック関数が設定されていたら呼び出す
  const callBack = options.callBack;
  if(typeof callBack === 'function') {
    callBack();
  }
}

関数の呼び出し

任意の場所で以下を実行し関数を呼び出してください。
ターゲット以外は任意設定です。

fadeAnime({
  //ターゲット設定(必須)
  target: target,
  //↓は任意設定
  //アニメーションの種類 'fadeIn' or 'fadeOut' or 'fadeToggle'(任意 デフォルト:'fadeToggle')
  animeType: 'fadeToggle', 
  //アニメーション時間 ミリ秒(任意 デフォルト:400)
  duration: 400,
  //Web Animations APIで設定できるイージング(任意 デフォルト:ease)
  easing: 'ease',
  //fadeIn表示後に付与されるcss displayの値(任意 デフォルト:block)
  displayStyle: 'block',
  //アニメーション後に呼び出すコールバック関数(任意 デフォルト:null)
  callBack: () => {
    console.log('fadeアニメ終了');
  }
});

requestAnimationFrame() 版

codepen

See the Pen fadeIn fadeOut fadeToggle Vanilla JS (Easing support) by web_nak (@web_walking_nak) on CodePen.

JavaScript

/**
 * requestAnimationFrame()によるfadeIn・fadeOut・fadeToggleアニメーション処理関数
 * @type {Object}
 * @param {HTMLElement} target ターゲット設定(必須)
 * @param {string} animeType アニメーションの種類 'fadeIn' or 'fadeOut' or 'fadeToggle'(任意 デフォルト:'fadeToggle')
 * @param {number} duration アニメーション時間 ミリ秒(任意 デフォルト:400)
 * @param {string} easing 'Web Animations API'で設定できるイージング(任意 デフォルト:ease)
 * @param {string} displayStyle 'fadeIn'表示後に付与されるcss displayの値(任意 デフォルト:block)
 * @param {function} callBack アニメーション後に呼び出すコールバック関数(任意 デフォルト:null)
 */
function fadeAnime(animeType, elm, duration, callBack) {
  'use strict';
  //fadeToggle分岐
  if (animeType === 'fadeToggle') {
    animeType = getComputedStyle(elm).display === 'none' ? 'fadeIn' : 'fadeOut';
  }

  //既に表示されている or 実行中だった場合は処理しない
  if (
    //fadeIn 既に表示されている or 実行中だった場合は処理しない
    (animeType === 'fadeIn' && (getComputedStyle(elm).display !== 'none' || elm.classList.contains('is-fade-busy')))
    //fadeOut 既に非表示 or 実行中だった場合は処理しない
    || (animeType === 'fadeOut' && (getComputedStyle(elm).display === 'none' || elm.classList.contains('is-fade-busy')))
    //有効なキーワードではない場合も処理しない
    || (animeType !== 'fadeIn' && animeType !== 'fadeOut')
  ) {
    return false;
  }
  //重複処理対策class追加
  elm.classList.add('is-fade-busy');


  /*
    イージング設定
      ・参考サイト
        https://github.com/danro/jquery-easing/blob/master/jquery.easing.js
        
【JavaScript】スムーススクロールをjQueryを使わずにシンプルに実装する【Vanilla JS】
「t:アニメーションの経過時間」「b:始点」「c:変化量」「d:変化にかける時間」 */ function easing(t, b, c, d) { return c * (0.5 - Math.cos((t / d) * Math.PI) / 2) + b; } //アニメーション分岐 let fadeFunc = null; if(animeType === 'fadeOut') { //初期設定 elm.style.opacity = 1; //アニメーション関数設定 fadeFunc = function(elapsedTime,duration) { //easing(「アニメーションの経過時間」,「始点」,「変化量」,「変化にかける時間」) return 1 - easing(elapsedTime, 0, 1, duration); } } else if(animeType === 'fadeIn') { //初期設定 elm.style.display = 'block'; elm.style.opacity = 0; //アニメーション関数設定 fadeFunc = function(elapsedTime,duration) { //easing(「アニメーションの経過時間」,「始点」,「変化量」,「変化にかける時間」) return easing(elapsedTime, 0, 1, duration); } } //アニメーション開始時間 const start = new Date(); function mainAnime() { //イベント発生後の経過時間 let elapsedTime = new Date() - start; //アニメーション終了処理 if (elapsedTime > duration) { //opacity削除 elm.style.removeProperty('opacity'); //実行中class削除 elm.classList.remove('is-fade-busy'); if(animeType === 'fadeOut') { //要素を非表示 elm.style.display = 'none'; } //コールバック関数が設定されていたら呼び出す if(typeof callBack === 'function') { callBack(); } //処理を終了 return false; } //アニメーション実行処理 elm.style.opacity = fadeFunc(elapsedTime,duration); requestAnimationFrame(mainAnime); } //アニメーション初回呼び出し requestAnimationFrame(mainAnime, callBack); }

関数の呼び出し

任意の場所で以下を実行し関数を呼び出してください。

//フェードイン
fadeAnime('fadeIn', 対象要素, アニメーション時間 [, コールバック関数(任意)]);

//フェードアウト
fadeAnime('fadeOut', 対象要素, アニメーション時間 [, コールバック関数(任意)]);

//フェードイン・アウトテスト
fadeAnime('fadeToggle', 対象要素, アニメーション時間 [, コールバック関数(任意)]);

動作サンプル

HTML

<button id="js-fade-in">fadeIn</button>
<div class="js-fade-in-elm">fadeIn要素</div>

<button id="js-fade-out">fadeOut</button>
<div class="js-fade-out-elm">fadeOut要素</div>

<button id="js-fade-toggle">fadeToggle</button>
<div class="js-fade-toggle-elm">fadeToggle要素</div>

CSS

/* fadeIn用設定 */
.js-fade-in-elm {
  display: none;
}

JavaScript

イージング設定や処理速度、コールバック関数(任意)はサイトに合わせて編集してください。

//【Web Animations API 版】
//フェードインテスト
const fadeInElm = document.querySelector('.js-fade-in-elm');
document.getElementById('js-fade-in').addEventListener('click', ()=> {
  //フェードイン呼び出し
  fadeAnime({
    target: fadeInElm,
    animeType: 'fadeIn',
    duration: 400,
    easing: 'ease',
    displayStyle: 'block',
    callBack: () => {
      console.log('fadeIn終了');
    }
  });
});

//フェードアウトテスト
const fadeOutElm = document.querySelector('.js-fade-out-elm');
document.getElementById('js-fade-out').addEventListener('click', ()=> {
  //フェードアウト呼び出し
  fadeAnime({
    target: fadeOutElm,
    animeType: 'fadeOut',
    callBack: () => {
      console.log('fadeOut終了');
    }
  });
});

//フェードイン・アウトテスト
const fadeToggleElm = document.querySelector('.js-fade-toggle-elm');
document.getElementById('js-fade-toggle').addEventListener('click', ()=> {
  //フェードイン・アウト呼び出し
  fadeAnime({
    target: fadeToggleElm,
    animeType: 'fadeToggle'
  });
});


//【requestAnimationFrame() 版】
//フェードインテスト
const fadeInElm = document.querySelector('.js-fade-in-elm');
document.getElementById('js-fade-in').addEventListener('click', function() {
  /*
    ・フェードイン呼び出し
      fadeAnime('fadeIn', 対象要素, アニメーション時間 [, コールバック関数(任意)]);
  */

  fadeAnime('fadeIn', fadeInElm, 400, function() {
    console.log('fadeIn終了');
  });
});

//フェードアウトテスト
const fadeOutElm = document.querySelector('.js-fade-out-elm');
document.getElementById('js-fade-out').addEventListener('click', function() {
  /*
    ・フェードアウト呼び出し
      fadeAnime('fadeOut', 対象要素, アニメーション時間 [, コールバック関数(任意)]);
  */
  fadeAnime('fadeOut', fadeOutElm, 400, function() {
    console.log('fadeOut終了');
  });
});

//フェードイン・アウトテスト
const fadeToggleElm = document.querySelector('.js-fade-toggle-elm');
document.getElementById('js-fade-toggle').addEventListener('click', function() {
  /*
    ・フェードイン・アウト呼び出し
      fadeAnime('fadeToggle', 対象要素, アニメーション時間 [, コールバック関数(任意)]);
  */
  fadeAnime('fadeToggle', fadeToggleElm, 400, function() {
    console.log('fadeToggle終了');
  });
});