LoginSignup
3
2

More than 3 years have passed since last update.

スケルトンスクリーン。cssだけでなくイベント発火までの解説

Last updated at Posted at 2019-08-16

やりたいことの流れ

以下intersectionObserverAPIで要素が入ってきたら発火するイベント順
1. 表示したい要素の画像部分(読み込みに時間がかかる場所)だけスケルトンスクリーンにするため、css用のクラス付与(imgタグを非表示にして背景をアニメーションでローディングしてる感じに動かす)
2. バナー画像を取得する関数の実行
3. window.addEventListener('load', function() {})イベントで画像読み込みが終わったら、非表示にするために付与したクラスを削除。※最初はsetTimeoutで秒数を設定し発火タイミングを設定したが、これだと画像を読み込みが完了したかと関係ないときに発火してしまう。

結果

動画イメージ

画像イメージ
skelton.png

ソース全容

lazyload.js
document.addEventListener("DOMContentLoaded", function () {
  let lazyObjects = [].slice.call(document.querySelectorAll(".lazy img, .lazy, .businessList__thumb > img"));
  // IntersectionObserver対応ブラウザのみ実行
  if ("IntersectionObserver" in window) {
    // IntersectionObserverオブジェクト生成
    let lazyObjectsObserver = new IntersectionObserver(function (entries, observer) {
      entries.forEach(function (entry) {
        if (entry.isIntersecting) {
          let lazyObject = entry.target;
          if (lazyObject.dataset.hasOwnProperty('bnr01') || lazyObject.dataset.hasOwnProperty('bnr02')) {

            $('.bnrList__link').addClass('loading__bnr animate__bnr');
            // 1. intersectionObserverAPIで要素が入ってきたらloading__bnr animate__bnrクラスを付与してimgタグを非表示にしておく
            // この時点でアニメーションがスタートする

            var loadBnr = new Promise(function () {
              // 2. バナー画像を取得する関数を記述
            })

            loadBnr
              .then(
                window.addEventListener('load', function () {
                // 3. 画像の読み込みが終わったら先ほど付与したクラスを削除(通常のデザインに戻す)
                  $('.bnrList__link').removeClass('loading__bnr').removeClass('animate__bnr');
                })
              )
              .catch(function () {
                console.log('Promise 失敗')
              })
          }
          lazyObject.classList.remove("lazy");
          lazyObjectsObserver.unobserve(lazyObject);
        }
      });
    });
    lazyObjects.forEach(function (lazyObject) {
      lazyObjectsObserver.observe(lazyObject);
    });
  }
});


1. intersectionObserverAPIで要素が入ってきたら、表示したい要素の画像部分(読み込みに時間がかかる場所)だけスケルトンスクリーンにするため、スケルトンスクリーンのアニメーション用のクラス付与(imgタグを非表示)

intersectionObserverAPIに関しては他の記事が分かりやすかったので、参考にしてみてください。

参考:Intersection Observer API を使った画像の遅延読み込み
参考:イメージと動画の遅延読み込み

要素が画面に入ってきたら、対象画像を非表示にして、アニメーションをスタートさせます。

lazyload.js
$('.bnrList__link').addClass('loading__bnr animate__bnr');
example.css
.loading__bnr {
  height: 130px;
  width: 100%;
  margin: 0 auto;
  border-radius: 5px;
}
.loading__bnr img {
  display: none;
}

/* animation */
.animate__bnr {
   animation : loading 2s infinite;
   background: linear-gradient(to right, #eff1f3 4%, #e2e2e2 25%, #eff1f3 36%);
   background-size: 1000px 100%;
}
@keyframes loading {
  0% {
    background-position: -1000px 0;
  }
  100% {
    background-position: 1000px 0;
  }
}

参考: The Loading Shimmer!

2. バナー画像を取得する関数を記述

各々記載ください

3. window.addEventListener('load', function() {})イベントで画像読み込みが終わったら、非表示にするために付与したクラスを削除。

lazyload.js
window.addEventListener('load', function () {
  // 3. 画像の読み込みが終わったら先ほど付与したクラスを削除(通常のデザインに戻す)
  $('.bnrList__link').removeClass('loading__bnr').removeClass('animate__bnr');
})

感想

  • window.addEventListener('load', function() {})にすると該当画像だけじゃなくて、他のリソースも取得しないと発火しないから、画像表示が遅くなるのでは?という疑問がある。 実際に実装するとそこまでリソース読み込みに時間がかかららなかったからOKとしている(window.addEventListener('load', function() {})を理解していないだけかも、サービスワーカーにキャッシュさせているから?)。よりよい記述方法があれば知りたい。とりあえず実装してみて改善していく方法で進めようと思う。
  • デザイン的に見せたいだけでなく、機能的にしないと意味がないという認識。スケルトンスクリーン入れるためにページスピードが下がったり、画像の表示が遅くなるなら(本当は画像を取得できているのに、スケルトンスクリーンを表示し、そこの切り替えタイミングどう制御するのかという課題が解決できていないなら)入れなくてもよいかもと思う。

参考

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2