3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

複数の画像を生成してローカルに保存

Last updated at Posted at 2020-07-08

HTML 要素を画像化した複数のファイルをローカルの専用サーバーに送って保存します。

シリーズの記事です。

  1. dom-to-imageを試す
  2. html2canvasを試す
  3. 複数の画像を生成してローカルに保存 ← 今回の記事

ライブラリ

前回まで同じ目的の2つのライブラリを比較しました。どちらも Firefox では文字サイズが変わってレイアウトが崩れるため、今回は Chrome を前提に進めます。

テキストだけを含む要素の画像化ではどちらを使ってもほとんど差がありませんでした。XMLHttpRequest でファイルを送るには Blob に変換する必要があるため、その機能をサポートしている dom-to-image を使用します。

HTML 要素を画像化した Blob の生成は簡単にできます。

let blob = await domtoimage.toBlob(src);

サーバー

以下の記事で作ったファイルを受け取るサーバー (server.ts) を使用します。

ファイルを受け取りたいディレクトリで server.ts を起動して待ち受けます。

スクリプト

画像を生成するための描画は済んでいるという前提で、送信に関係するコードを示します。

log

コンソールを模した div にログを出力します。

function log(str) {
  result.appendChild(document.createTextNode(str));
  result.appendChild(document.createElement("br"));
}

以下の記事のコードを簡略化したものです。

XMLHttpRequest

XMLHttpRequest を Promise 化します。

function send(method, url, blob) {
  log(`${method} ${url}`);
  return new Promise((resolve, reject) => {
    let req = new XMLHttpRequest();
    req.open(method, url);
    req.onload = () => {
      let result = `${req.status} ${req.response}`;
      if (req.status == 200) {
        log(result);
        resolve();
      } else {
        reject(result);
      }
    };
    req.onerror = () => reject("error");
    req.onabort = () => reject("abort");
    req.ontimeout = () => reject("timeout");
    req.send(blob);
  });
}

正常終了は resolve、エラーは reject を呼びます。

4種類のコールバックをチェックしているのは、以下の記事を参考にしました。

以下の4つのイベントは同時に発火することがありません

  • onload
  • onerror
  • onabort
  • ontimeout

よって、4つ全て指定しないと通信の終了を取りこぼす可能性があります

送信

画像の Blob を XMLHttpRequest で送信します。画像をすべて送信したら、サーバーを終了させます。

try {
  result.innerHTML = "";
  for (let [i, src] of sources) {
    let blob = await domtoimage.toBlob(src);
    await send("POST", `http://127.0.0.1:8080/${i}.png`, blob);
  }
  await send("GET", "http://127.0.0.1:8080/end");
} catch (e) {
  log(e);
  log("Aborted.");
}

成功した時の状況です。

クライアント側(ブラウザ)
POST http://127.0.0.1:8080/0.png
200 <m>created</m>
POST http://127.0.0.1:8080/1.png
200 <m>created</m>
POST http://127.0.0.1:8080/2.png
200 <m>created</m>
POST http://127.0.0.1:8080/3.png
200 <m>created</m>
POST http://127.0.0.1:8080/4.png
200 <m>created</m>
GET http://127.0.0.1:8080/end
200 <m>bye!</m>
サーバー側
$ deno run --allow-net --allow-read --allow-write server.ts
http://127.0.0.1:8080
127.0.0.1 OPTIONS /0.png
127.0.0.1 POST /0.png
0.png 18321 created
127.0.0.1 OPTIONS /1.png
127.0.0.1 POST /1.png
1.png 18537 created
127.0.0.1 OPTIONS /2.png
127.0.0.1 POST /2.png
2.png 18706 created
127.0.0.1 OPTIONS /3.png
127.0.0.1 POST /3.png
3.png 18573 created
127.0.0.1 OPTIONS /4.png
127.0.0.1 POST /4.png
4.png 18562 created
127.0.0.1 GET /end

ローカルで閉じた通信です。サーバーを起動したディレクトリには受信したファイルがあります。

$ ls
0.png  1.png  2.png  3.png  4.png

サンプル

文章の選択個所を変えた画像を生成するサンプルです。

[Send to Local] を実行するには、server.ts を起動しておく必要があります。

感想

ブラウザを画像生成のエンジンとして使えるようになったので、文字中心のコンテンツを動画化するのに威力を発揮しそうです。一部の色を変えるような単純な効果でも、手動だと大変でしたが、自動化できれば大幅に省力化できます。

以前書いた記事では連続画像を効果的に生成する方法がないため紙芝居レベルに留まっていましたが、今回の方法を応用すれば動きを付けることもできそうです。

3
3
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
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?