最近全画面表示のパララックスを使ったちょっぴりリッチなコンテンツを作る機会があったのですが、要素や初期化処理がそこそこ多くてローディングがないとサイトに訪問した瞬間に何かグチャグチャ動作するのが見えてかっこ悪い。

「読み込み中…56%」とかそんな感じのゲージとかを表示したい時なんかにも実際のロード状況がわからないと作れないなぁということで画像のプリロードなどのロード処理について調べてみました。

ローディング処理について考える

jQueryを始めたころ、私は「.ready()」でページ内のすべての読み込みが終わってから処理が始まるのだと思っていました。

 $(document).ready(
    // ページ読み込み完了してから処理が開始されるはず
    alert('konnichiwa!');
  });

初心者の私は「DOM = htmlのページ」という風に理解していたので、「ローディングしようにも、処理が始まった時点で画像なんかも全部読み込まれているんじゃないのかね?」と思っていました。が、それは間違いで、画像をたくさん貼付けたような複雑なページだと、画像が全部表示されていないうちに「konnichiwa!」というダイアログが出てきて困惑したものです。

jQueryの「.ready()」は、htmlのタグが読み込まれた時点で処理を開始します。つまり画像や、外部ファイルの読み込みを待ちません。
ローディングは「DOMツリーを読み込んだ直後〜画像・外部ファイルを読み込み&初期化が終わるまで」表示されてほしいわけです。

このあたりの、「処理がいつどう始まってどう進んでいくのか」というあたりの内容は始めたばかりの自分にはかなりの壁がありました。

window.onload = function(){ //処理 };
$(document).ready(function(){ //処理 });
$(function(){ //処理 });

このあたりの違い、わかりますか?ローディングの話は上のような処理開始の流れの知識が必要だったり、JavaScriptのオブジェクトの理解が必要だったりとちょっと難しいです。今回は自分の備忘記事と割り切って解説は省いてポイントだけざっくり残しておきます。ちなみに上の関数の違いは…。

  • window.onloadは画像・外部ファイルを含むすべての読み込みを待って実行
  • 下二つは同じ意味で、DOMの読み込みのみを待って実行

ローディング処理

ローディングの流れは次のような感じになります。

  1. DOM読み込み完了直後ローディングを表示。
  2. ロード完了チェック(画像ロード完了、スクリプト任意の初期化処理完了、外部ファイルロード完了など)を定期的に監視開始(setInterval)
  3. 画像のプリロード処理
  4. 外部ファイルロード処理
  5. スクリプト任意の初期化処理
  6. すべてのロード処理が完了したら、ローディングを非表示にする
/* 初期化部分 */
self.loadTimerID;  // タイマー
entity.settings.loading = self.append('<div class="loading">Loading</div>').find('.loading');
self.loadTimerID = setInterval(loadingCtrl, 500);

/* ローディング関数 */
var loadingCtrl = function() {
  // 監視する完了フラグの判定
  if(self.preloadCompleteFlg && self.setupCompleteFlg) {
    clearTimeout(self.loadTimerID);
    // ローディングの非表示
    entity.settings.loading.delay(300).fadeOut('slow');
  }
}

画像のプリロード処理

「画像のプリロード」と検索すると、「.load()」イベントを使う例が多く出てくるがjQuery1.8で非推奨になっている(Deprecated | jQuery API Documentation(公式))ため使わない方がいい。

以下は、「self」の子要素の「img」タグ画像全てをプリロードする例の一部。背景画像などは含まれていないので、場合に合わせて「preLoadCtrl関数」の引数にimageのsrcリストを渡すなどアレンジが必要。

/* 初期化部分 */
self.preloadCompleteFlg = false;  // 画像プリロード完了フラグ
entity.loadImgLen = self.find('img').length;  // ローディング対象画像数
preLoadCtrl();

/* プリロード関数 */
preLoadCtrl.progress = 0;
function preLoadCtrl() {
  if(entity.loadImgLen > 0) {
    $('img', self).each(function(){
      // 画像のプリロード
      var image = new Image();

      image.onload = function(){
        preLoadCtrl.progress++;
        // 画像読み込み完了判定
        if(preLoadCtrl.progress == entity.loadImgLen){
          self.preloadCompleteFlg = true;
        }
      };

      // 次の命令で画像プリロードが開始されるので、必ずimage.onloadイベント設定後に書く。
      image.src = $(this).attr('src');
    });
  } else {
    self.preloadCompleteFlg = true;
  }
}

「preLoadCtrl.progress」というのが、読み込み対象の画像総数に対する完了枚数になるので、ローディンの%表示をするのに使えるはず…。まだ実装したことはありませんが。。

スクリプト任意の初期化処理

画像のロードほどのテクニックは特にないが、初期化の処理にanimateなどがあると、その命令文を通過しただけでは処理が完了したかどうかが判定できない。
厳密に完了を判定するには、animateなどのコールバック関数で完了フラグをたてるようにする必要がある。