2019年5月23日に開催した「クロコラボ #22 JavaScriptでローディングを作ってみよう」のレポートです。
ローディングの素材
ローディングの素材は、次のジェネレータを利用しました。
Preloaders.net – Loading GIF, SVG & APNG (AJAX loaders) generator
ファイル形式は、GIF、APNG、SVGで選択でき、色やサイズ、アニメーションのスピードまで指定してダウンロードすることができます。今回はSVGを利用しました。
ちなみに、APNGという画像形式については、次のICS MEDIAさんの記事がわかりやすかったです。
GIFアニメからAPNGの時代に! 次世代画像形式APNGを使いこなそう – ICS MEDIA
シンプルなローディングを作ってみる
さて、実際にローディングの実装です。まず単純に、全てのコンテンツが読み込まれたら、ローディングを終了することを考えました。ローディングは終了時にフェードアウトすることにします。jQueryでは fadeOut() を使っていましたが、 JavaScriptでは少しややこしい記述が必要でした。
参考サイト
脱jQuery .fadeIn() .fadeOut() .slideUp() .slideDown() _ q-Az
jQuery
$('#loding').fadeOut();
JavaScript
let begin = new Date(); //開始時刻 タイムスタンプの取得
const loadingCotent = document.getElementById('loading'); //ローディング要素の取得
const loadingTime = 1000; // フェードインの時間
let id = setInterval(function() {
let current = new Date() - begin; //beginから現在の時間
if (current > loadingTime) { //フェードインの時間を超えたら要素を消す
clearInterval(id);
current = loadingTime; //今の時間とフェードインの時間の差をなくす
loadingCotent.style.display = "none"; //要素を消す
}
loadingCotent.style.opacity = 1 - (current / loadingTime) ; //割合に応じて透過していく
}, 10); //100分の1秒間隔で繰り返し実行
実際のjQueryでこのような処理の仕方なのかは確認していませんが、フェードアウトひとつでも、裏でこのような処理をされているんですね。普段jQueryでの実装がほとんどなので、jQueryからJavaScriptへの変換は意外と難しかったです。
実際のコード
See the Pen loading JavaScript by kurokoroll (@kurokoro) on CodePen.
ここで気になるのは、フェードアウトの終わりが滑らかでないところです。おそらく、現在の時間とフェードインの時間の端数を無くしてる箇所あたりが原因ではないかと推測します。
プログレスバー付きのローディングを作る
次に、指定した画像が読み込まれたら、ローディングを非表示にするという方法で実装してみました。今回は、フェードアウトをCSSのtransitionで実装し、JavaScript側でクラスを外す処理をしています。また、transitionendを使って、トランジションが終わったらローディング自体を消す処理も加えています。
実際のコード
See the Pen loading JavaScript by kurokoroll (@kurokoro) on CodePen.
キャッシュされるとローディング時間が短くなりわかりにくいのですが、ローディング画面は次のようになります。
この場合、画像の枚数に応じて割合が変わるので、100%を画像の枚数分で分割されたパーセントが表示されます。今回は画像が6枚なので、17%、33%、50%、67%、83%、100%となりました。これでは、0%から17%、17%から33%と急に値が飛んでしまいます。
では、枚数での割合ではなく画像やコンテンツの容量で、パーセントを表示できないのでしょうか?
プラグインをのぞいてみる
画像の容量を取得するところで苦戦しました。そこで、既存のプラグインのコードをのぞいてみることで、何か方法はないかを探ってみることにしました。
参加者さんが「preload-it」というプラグインを見つけてくださいました。サンプルのページは動画でしたが、画像でも同様にでき、割合が「1%、2%…」と増えていくことから、ファイルの容量を取得してパーセンテージを出してそうなので、コードをのぞいてみることにしました。GitHubのソースをのぞいてみたところ、XMLHttpRequest というものが使われていました。
な、なんだそれは?
XMLHttpRequest (XHR) オブジェクトを使用すると、サーバーと対話することができます。ページ全体を更新する必要なしに、データを受け取ることができます。これでユーザーの作業を中断させることなく、ウェブページの一部を更新することができます。 XMLHttpRequestは AJAX プログラミングで頻繁に使用されます。
XMLHttpRequest – Web API _ MDN
なるほど。Ajax通信(非同期通信)で利用されるものなんだ!
また、responseType というものに「blob」が指定されていたのでこちらも調べて見ました。まずはresponseType。
XMLHttpRequest オブジェクトの responseType プロパティで、サーバーに期待する応答の種類を変更することができます。設定可能な値は空文字列 (既定), “arraybuffer”, “blob”, “document”, “json”, “text” です。
バイナリデータの送信と受信 – XMLHttpRequest _ MDN
ふむ。そして「blob」ってのはなんだ?
The response is a Blob object containing the binary data.
XMLHttpRequest.responseType – Web API _ MDN
どうやら、バイナリデータのことのようです。
つまり、Ajaxで画像の容量をバイナリデータで取得することで、割合を求めることができるようです。今回の勉強会内では試せなかったのですが、これを使えば、画像やコンテンツの容量でパーセントを表示することができそうです。
プラグインを使ってみる
別の参加者さんが見つけてくださったプラグイン「PACE」を試してみます。pace.jsを読み込み、用意されたテーマをCSSに貼り付ければ、特にJavaScriptを書くこともなく実装されました。簡単!
実際のコード
See the Pen Loading with PACE by kurokoroll (@kurokoro) on CodePen.
はじめに作った2つのものと違って動的にローディングを表示しているので、JavaScriptを止めても内容がみれるのは良いなと思いました。
でも、ローディング終了時の表示が滑らかでないので、できればフェードアウト等したいところ。そして、ローディング中にスクロールできてしまうのをなんとかしたい…などなど、少し調整が必要ですね。
まとめ
そもそも、表示が速いページならローディングは必要ないかもしれません。ただ、速く表示されるように構築しても、ユーザーによってはインターネットが遅かったり、電波がよくない環境にいて、速く表示されないケースもあると思います。読み込みがされているのか、どのくらい読み込まれてるのかを判断するにも、ローディングがあると親切かなと思います。
そして、jQueryは偉大!というのを実感。脱jQueryとかオワコンとか見かけますが、私が作る案件の規模では、JavaScriptで時間をかけて構築するよりも、jQueryなどのライブラリで効率的に作る方がいい場合が多いかなと。でも、ちゃんとJavaScript書きたい!書けるようになりたい!ので、引き続き勉強会で取り上げていきたいなと思っています。
ただ、結局ベストプラクティスは何なのか?ということろでモヤっとしてしまうので、そこはどうやって見つけていったらいいものか、模索中です。