プログラミング

lab.js で画像を事前に読み込ませる

はじめに

画像の読み込みには時間がかかります。これは実験の際にトラブルを引き起こすことがあります。
たとえば、実験で画像を2秒間だけ表示させたいのに、最初の1秒間は画像が読み込み中のため画像が表示されず(あるいは読み込み中の画像の一部分だけが表示され)、意図した通りの呈示時間で画像を表示できないことがあります。

この問題を解決するには、実験で使用するすべての画像を事前に読み込ませる処理を加えるとよいです。

問題の所在

具体例を通して説明します。
今回の例ではlab.jsのHTML Pageコンポーネントにimgタグを使って画像を表示させています。
画像のファイル名は01.jpg, 02.jpg, ..., 10.jpg という名前にしておき、Loopのパラメータを使って01から10までの画像名を生成します。

完成版のファイルをこちらに用意したので参考にしてください。

10枚のjpg画像をloopを使って1秒ずつ連続で表示するとします。
使用画像としてノイズの上に数字が乗ったものを用意しました(1枚目と2枚目の画像の例を下に掲載)。
それぞれの画像のファイルサイズは2.4MBほどです。軽い画像ではないですが、かといって異常に重いというほどでもない画像です。

1枚目の画像

2枚目の画像

このような想定でlab.jsのプログラムを作成・実行すると、次のような表示のされ方になることがあります。

各画像の画面は1秒ずつ表示されていますが、画像の読み込みが間に合っていないため、画像の上の方しか描画されていません。

この問題への対処方法として、

1. 個別の画像のファイルサイズを可能な限り小さくする
2. 画像を事前に読み込みさせる

の2つがあります。
2が根本的な解決になりますが、1も併せてやっておくとよいでしょう。

1. 個別の画像のファイルサイズを可能な限り小さくする

画像のファイルサイズを小さくすることで画像の読み込みがすぐに終わるようにします。
2つのステップがあります。

Step1: 画像のピクセルサイズを実験で表示する際のピクセルサイズに合わせる
画像を画面上で800x600ピクセルのサイズで表示する実験なのに、用意している画像のサイズがそれを超えるピクセルサイズ(たとえば1600x1200ピクセル)である場合は、画像をリサイズして実験で使用するサイズ(この例では800x600ピクセル)に縮小しましょう。画像編集ソフトを使えばできます。

Step2: 画像の圧縮をする
上記の作業をした上で、さらにファイルサイズを小さくするするために画像の圧縮を行いましょう。
たいていの画像編集ソフトではJPG画像を出力する際に圧縮率が設定できるので、その値を調整して画像のファイルサイズを小さくできます(強く圧縮するほどフィアルサイズは小さくなりますが画質も劣化します)。
あるいは、オンラインの画像圧縮サービスを使うのも手軽な方法です。
私がよく使うのは https://tinypng.com/ です。
ここに画像をアップすると圧縮された画像を作成してくれます。画像のファイルサイズをおよそ半減させることができます(画質の劣化もほとんど感じません)。

画像のファイルサイズを500KB未満くらいまで小さくできれば画像の読み込みはかなり早くなると思います。それでも、読み込みにはいくらかの時間がかかるので、画像の呈示時間を厳密に守りたい場合には次に説明する画像の事前読み込みが必要になります。

2. 画像を事前読み込みさせる

「javascript 画像 プリロード」などと検索すれば画像の事前読み込みのやり方を見つけることができます。

今回の例では実験の冒頭で事前読み込み処理を行うHTML Pageコンポーネントを用意します。
ContentタブでRaw HTMLを挿入し、以下の内容を記述します。
また、画面下部に表示されるContinueボタンはHide submit buttonを選んで非表示にします。

<p>データを読み込んでいます。しばらくお待ちください。</p>
<p class="loadProgress"></p>
<div class="loadComplete"><button>次へ進む</button></div>

Scriptsでは、実行タイミングの選択肢として run を選びます。
そして以下の内容を記載します。

let imgDir = "static/";
let imgPaths = []
for (let i = 1; i <= 10; i++) {
  imgPaths.push(imgDir + String(i).padStart(2, '0') + ".jpg");
}

async function load_image(path){
  const t_img = new Image();
  return new Promise(
    (resolve) => {
      t_img.onload = () => {
        resolve(t_img);
      }
      t_img.src = path;
    }
  )
}

let pProgress = document.getElementsByClassName('loadProgress')[0];
let divLoadComplete = document.getElementsByClassName('loadComplete')[0];
divLoadComplete.style.display = "none";
for (i = 0; i < imgPaths.length; i++){
  pProgress.innerText = Math.trunc(100 * i / imgPaths.length) + "%";
  let img = await load_image(imgPaths[i]);
}
divLoadComplete.style.display = "block";
this.end();

実験で使用するすべての画像のパスを imgPaths という変数に配列として作成しています。
load_image関数が画像を事前読み込みするためのものです。
pProgress や divLoadComplete は画面上に読み込みの進行状況を表示するためのHTML要素を書き換えるためのものです。
for文を使って10枚分の画像を読み込み、それが終わると this.end(); によってこのコンポーネントを修了し次のコンポーネントに進んでいます。

また、10枚分の画像をstatic directoryにアップロードしておきます(こちらの記事も参照)。

プログラム全体の詳細については記事冒頭に掲載した完成版ファイルを使って確認してください。

結果として、次のような画像事前読み込みが行われます。
開始ボタンを押した後に事前読み込みの処理が始まり、進行状況がパーセントで表示されます。すべての読み込みが終わると次の画面に進みます。

事前読み込みをしたことで、画像が問題なく表示されました。

補足事項1:画像を自前のサーバーにアップする場合

以上の説明では画像をstatic directoryにアップロードしていました。
しかし、私は普段の実験作成ではstatic directoryは使用していません。
なぜなら、static directoryには以下のような制限があり、不便だからです。

- 最大で合計50MBまでしかファイルをアップロードできない。
- 個別のファイルも3MB未満くらいまでのファイルサイズである必要がある。

私は自分でレンタルサーバーを契約しているので、そこに画像をアップロードしています。
そうすれば上記のような制限を気にせずに実験が作成できます。

サーバーに画像をアップする方式の場合、lab.jsのプログラムに変更を加える必要があります。

まず、Preloadという名前のコンポーネントのScriptsの欄で、冒頭部分が以下のようになっています。

let imgDir = "static/";

これを、たとえば以下のように書き換えます。

let imgDir = "https://hogehoge.com/img/";

https://hogehoge.com/img/ という箇所はあなたの事情に合わせて書き換える必要があります。hogehoge.com という場所にimgという名前のフォルダを作成し、その中に画像ファイルをアップロードしている場合はそのようにする、ということです。

次に、Loop内にあるimageという名前のコンポーネントで、Content欄は以下のようになっています。

<img src=${"static/" + parameters.imgID + ".jpg"} width="100%"">

これは具体的には <img src="static/01.jpg" width="100%"> といった状態になるものです。

これを、たとえば以下のように書き換えます。

<img src=${"https://hogehoge.com/img/" + parameters.imgID + ".jpg"} width="100%"">

これは具体的には <img src="https://hogehoge.com/img/01.jpg" width="100%""> といった状態になるものです。 https://hogehoge.com/img/ という箇所は先ほどと同様、あなたが画像をアップしたサーバーのディレクトリに書き換える必要があります。

補足事項2:事前読み込み処理の動作確認

画像を一度ブラウザに読み込むと、その画像のデータはブラウザに保存(キャッシュ)されます。
キャッシュされた状態で実験を実行すると、画像呈示画面で画像が素早く表示されているのが「事前読み込みの処理が適切に行われたおかげ」なのか(そうであることが望ましい)、それとも「ブラウザのキャッシュを利用しているおかげで素早く表示されているだけ」なのか(その場合は事前読み込みの処理にバグがあっても画像が素早く表示されてしまいバグに気づくことができないので望ましくない)、区別がつかなくなります。

そこで、事前読み込み処理を含んだlab.jsのプログラムの動作確認をする際は、実験の実行前にブラウザのキャッシュを毎回消去する必要があります。
キャッシュの削除方法はブラウザごとに異なります。Chromeを使っている場合は「chrome キャッシュ消去」などでググると方法がわかります。

なお、この記事はlab.jsで作成した実験をオンライン実験として行う場合を想定しています。もし実験室実験として行うのであれば、その実験PCで実験プログラムを一度行ってブラウザに画像をキャッシュさせておけば画像は素早く表示されるので、事前読み込みの処理は不要です。

lab.jsに関する記事一覧

コメント

Copied title and URL