owned mediaウェブ制作に役立つコンテンツを発信中!

jQueryでよく使うメソッドをネイティブのJavaScriptに置き換えてみる#5:UI・アニメーション

最終更新日: Update!!
jQueryにはDOMの操作だけでなく、UIパーツの実装などに便利なアニメーションなどの動きを表現できるメソッドもたくさん用意されています。第5回目となる今回はそのようなメソッドをネイティブのJavaScriptに置き換えていきたいと思います。実際にこれらのメソッドはネイティブのJavaScriptで実装するとなるとかなり大変でした、、逆にjQueryに依存しがちな処理でもあると言えますね。そのためにも置き換えの方法を知っておいても損はないですね。  
プロパティを指定時間で変更:animate()
特定のCSSのプロパティなどを指定した時間をかけて変更、あるいは設定するメソッドが「animate()」です。これを使うとその名の通り指定したプロパティへアニメーションをつけながら変化させることができます。引数には順に「CSSプロパティ」「変化の時間」「イージングの設定」と指定していきます。 【jQuery】
$("#hoge").animate(
  { 'fontSize': 40 },
  2000,
  'linear'
);
  ネイティブのJavaScriptを使う場合には、簡単な方法として「CSSのtransitionプロパティ」を使う方法があります。これを使うことで短いコードで手軽に、且つより自然に見せることができます。ただし、transitionに対応していないプロパティに関してはアニメーションをつけることができません。そんな場合には「setInterval」を使って一定時間ごとに徐々に変化を与えていく方法が使えます。 【JavaScript】
// CSSのtransitionを使う場合
const element = document.getElementById('hoge');
element.style.cssText = 'font-size: 40px; transition: font-size 2s linear 0s;'

// setIntervalを使う場合
const element = document.getElementById('hoge');
const duration = 2000;
const begin = 16;
const finish = 40;
const startTime = new Date().getTime();
const stopTime = startTime + duration;
const timer = setInterval(() => {
  let currentTime = new Date().getTime();
  let progress = (currentTime - startTime) / duration
  let zoom = (finish - begin) * progress
  element.style.fontSize = `${begin + zoom}px`;
  current = new Date().getTime();
  if(1 < progress) {
    clearInterval(timer);
  }
}, 10);
  setIntervalを使う場合には、設定時間と経過時間の割合を進捗率として算出する必要があります。この進捗率に合わせて、CSSプロパティの値を徐々に変化させていくような設定をします。進捗率の算出方法ですが、「new Date()」を使って開始時刻と現在時刻から経過時間を割り出し、それを設定時間で割ることで算出することができます。そして進捗率が100%を超えた時点で「clearInterval」を使って一定時間の繰り返し処理を終了させます。  
フェードインでの表示・非表示:fadeIn() / fadeOut()
特定要素の透明度を徐々に変化させ、表示・非表示を切り替えるメソッドが「fadeIn()」と「fadeOut()」です。fadeIn()は徐々に表示させ、fadeOut()は徐々に非表示にさせることができます。引数には設定時間を入れることでスピードを調整できます。 【jQuery】
$("#hoge").fadeIn(2000);
$("#hoge").fadeOut(2000);
  ネイティブのJavaScriptを使う場合でも先ほどの、animate()と同じく設定時間と経過時間の割合から進捗率を算出する方法を使います。今回の場合には表示・非表示を切り替えるため、fadeIn()の場合にはデフォルトのスタイルとして、displayプロパティとopacityプロパティを設定して非表示にしておく必要があります。 【JavaScript】
// fadeIn
const element = document.getElementById('hoge');
element.style.cssText = 'display: block; opacity: 0;'
const duration = 2000;
const start = new Date().getTime();
const stop = start + duration;
const timer = setInterval(() => {
  let current = new Date().getTime();
  let progress = (current - start) / duration
  element.style.opacity = 0 + progress;
  current = new Date().getTime();
  if(1 < progress) {
    clearInterval(timer);
  }
}, 10);

// fadeOut
const element = document.getElementById('hoge');
const duration = 2000;
const start = new Date().getTime();
const stop = start + duration;
const timer = setInterval(() => {
  let current = new Date().getTime();
  let progress = (current - start) / duration
  element.style.opacity = 1 - progress;
  current = new Date().getTime();
  if(1 < progress) {
    clearInterval(timer);
    element.style.display = 'none';
  }
}, 10);
  fadeIn()、fadeOut()では、時間経過に合わせてopacityの値が変わり、fadeOut()の場合は設定時間になるとdisplayプロパティが切り替わるという仕組みになっています。この切り替わるタイミングは進捗率が100%を超えた時点になるので、clearIntervalの実行直後に行います。  
スライドしながらの表示・非表示:slideDown() / slideUp()
要素の高さがスライドするように変化するアニメーションを伴って表示・非表示を切り替えるのが「slideDown()」と「slideUp()」メソッドです。これもよく使われるメソッドではないでしょうか。引数には同じく設定時間を入れることでスピードを調整できます。 【jQuery】
$("#hoge").slideDown(2000);
$("#hoge").slideUp(2000);
  このメソッドの置き換えはかなり大変です。実際、jQueryの処理をデベロッパーツールで確認してみると、paddingとmarginとheightの高さが徐々に変化するという複雑なもので、そのためにはまず要素自体の「上下paddingを除いた高さ」「上下paddingの値」「上下marginの値」をそれぞれ取得する必要があります。かなり試行錯誤を繰り返したのですが、こちらのサイト(脱jQuery .fadeIn() .fadeOut() .slideUp() .slideDown())を参考になんとか再現することができました。   まず、slideDown()の場合ですがこれはかなり大変で、要素の高さと上下のpaddingとmarginの値を取得する必要があるのですが、初期状態で非表示となっているのでそのままでは取得できません。そのため「visibility」プロパティを使って非表示にし、高さの値を取れるようにします。その場合には高さの分だけレイアウトスペースが空いてしまうので「position」プロパティを使って表示場所を調整しレイアウトに影響が出ないようにします。 【JavaScript】
// slideDown
const element = document.getElementById('hoge');
const computedStyle = {
  paddingTop: 0,
  paddingBottom: 0,
  marginTop: 0,
  marginBottom: 0,
  height: 0
}
const init = () => {
  element.style.cssText = 'overflow: hidden; display: block; visibility: hidden; position: absolute;';
  computedStyle.paddingTop = parseFloat(window.getComputedStyle(element).getPropertyValue('padding-top'));
  computedStyle.paddingBottom = parseFloat(window.getComputedStyle(element).getPropertyValue('padding-bottom'));
  computedStyle.marginTop = parseFloat(window.getComputedStyle(element).getPropertyValue('margin-top'));
  computedStyle.marginBottom = parseFloat(window.getComputedStyle(element).getPropertyValue('margin-bottom'));
  computedStyle.height = element.clientHeight - parseFloat(window.getComputedStyle(element).getPropertyValue('padding-top')) - parseFloat(window.getComputedStyle(element).getPropertyValue('padding-bottom'));
}
const ready = () => {
  element.style.cssText = 'overflow: hidden; display: block; visibility: visible; position: static; height: 0; margin-top: 0; margin-bottom: 0; padding-top: 0; padding-bottom: 0;';
}
const duration = 2000;
const start = new Date().getTime();
const stop = start + duration;
const run = () => {
  return new Promise((resolve, reject) => {
    init();
    resolve();
  })
  .then(() => {
    return new Promise((resolve, reject) => {
      ready();
      resolve();
    })
  })
  .then(() => {
    return new Promise((resolve, reject) => {
      const timer = setInterval(() => {
        let current = new Date().getTime();
        let progress = (current - start) / duration
        element.style.paddingTop = `${computedStyle.paddingTop * (0 + progress)}px`;
        element.style.paddingBottom = `${computedStyle.paddingBottom * (0 + progress)}px`;
        element.style.marginTop = `${computedStyle.marginTop * (0 + progress)}px`;
        element.style.marginBottom = `${computedStyle.marginBottom * (0 + progress)}px`;
        element.style.height = `${computedStyle.height * (0 + progress)}px`;
        current = new Date().getTime();
        if(1 < progress) {
          clearInterval(timer);
        }
      }, 10);
      resolve();
    })
  })
}
run();

// slideUp
const element = document.getElementById('hoge');
element.style.overflow = 'hidden';
const computedStyle = {
  paddingTop: parseFloat(window.getComputedStyle(element).getPropertyValue('padding-top')),
  paddingBottom: parseFloat(window.getComputedStyle(element).getPropertyValue('padding-bottom')),
  marginTop: parseFloat(window.getComputedStyle(element).getPropertyValue('margin-top')),
  marginBottom: parseFloat(window.getComputedStyle(element).getPropertyValue('margin-bottom')),
}
const elementHeight = element.clientHeight - computedStyle.paddingTop - computedStyle.paddingBottom;
const duration = 2000;
const start = new Date().getTime();
const stop = start + duration;
const timer = setInterval(() => {
  let current = new Date().getTime();
  let progress = (current - start) / duration
  element.style.paddingTop = `${computedStyle.paddingTop * (1 - progress)}px`;
  element.style.paddingBottom = `${computedStyle.paddingBottom * (1 - progress)}px`;
  element.style.marginTop = `${computedStyle.marginTop * (1 - progress)}px`;
  element.style.marginBottom = `${computedStyle.marginBottom * (1 - progress)}px`;
  element.style.height = `${elementHeight * (1 - progress)}px`;
  current = new Date().getTime();
  if(1 < progress) {
    clearInterval(timer);
    element.style.display = 'none';
  }
}, 10);
  あとは先ほどと同じく、設定時間と経過時間の割合から進捗率を算出する方法で徐々に、heightとpadding、marginの値を調整していきますが、まず初めに表示状態の要素の情報を取得し、そのあとに処理を始めないといけないため、最終的にPromiseを使って直列の処理とすることで動くようになりました。   slideUp()の場合はもう少しシンプルで、単純に初期表示状態の情報を取得し、時間経過と合わせて徐々に0に近づけていき、設定時間に到達すると非表示にするという処理を加える形となります。  
  全5回にわたってjQueryでよく使うメソッドをネイティブのJavaScriptに置き換えた場合のコードをまとめてきました。一つ言えることはjQueryはとても便利なライブラリであるということですね! (こちらの記事もどうぞ) ・jQueryでよく使うメソッドをネイティブのJavaScriptに置き換えてみる#1:基本的な操作 ・jQueryでよく使うメソッドをネイティブのJavaScriptに置き換えてみる#2:値の取得・設定 ・jQueryでよく使うメソッドをネイティブのJavaScriptに置き換えてみる#3:要素の走査選択・絞り込み ・jQueryでよく使うメソッドをネイティブのJavaScriptに置き換えてみる#4:要素の移動と挿入・データ通信   ただ、jQueryに依存が高くなってしまうと、別のライブラリやフレームワークに移行する場合に大変な場合もありますので、ネイティブのJavaScriptを使った方法も覚えておくといいのではないでしょうか。また今回実際にjQueryからネイティブのJavaScriptに書き換えたということでjQueyのメソッドについてはもちろんのこと、JavaScriptについてもかなり勉強になったのは良かったですね。jQueryをよく使われる場合には一度試してみてもいいのではないでしょうか。   (参考にさせて頂いたサイト) 脱jQuery .fadeIn() .fadeOut() .slideUp() .slideDown() display: none;のとき要素のサイズが取得できない
  • はてなブックマーク
  • Pocket
  • Linkedin
  • Feedly

この記事を書いた人

Twitter

sponserd

    keyword search

    recent posts

    • Twitter
    • Github
    contact usscroll to top
      • Facebook
      • Twitter
      • Github
      • Instagram