🌁

HTMLとしてレンダリング・レイアウトした画像をPNGとして保存する

2022/08/06に公開

先日こんなツールを作成しました。

https://github.com/side-beach-city/SBC.ImageMaker

わたしが今まで配信しているポッドキャスト番組SBCast.では公開後に、その情報をInstagramにQRコード付きで紹介&YouTubeに音声+画像の動画をアップロードしていたのですが、それらをいちいちPC上のツールを使うことなくWeb上で作成できるツールです。

画像のレイアウトなどはCSSを使えば比較的簡単にできますので、それを画像として保存します。

結論

結論から言うとDOM to Imageというモジュールを使用します。

https://github.com/tsayen/dom-to-image

HTMLのレイアウトをして、保存したい要素を含むノードを引数にdomtoimage.toPng()などのメソッドを呼び出せばOKです。

document.querySelectorAll("button[data-role='saveimage']").forEach(v => v.addEventListener("click", async (e) => {
  const imgbase = document.getElementById("imgbase");
  const dl = document.createElement("a");
  dl.href = await domtoimage.toPng(imgbase, {
    width: imgbase.clientWidth,
    height: imgbase.clientHeight
  });
  dl.download="savefile.png";
  dl.click();
}));

なお、widthとheightを指定しない場合、とくにpositionがrelativeなどの値になっているノードを保存しようとしたときに画像サイズが期待通りの値にならないことがありますので、必ず設定しておきましょう。

注意点

このDom To Imageですが、公式ページのReadmeや、別途同じ処理について解説しているブログ記事にも書いてあるとおり、外部リソースを参照する際には、全てのファイルをダウンロード後、BASE64エンコードしてdata-urlとして挿入するという処理を行っています。
このため、外部リソースを使う必要がある時には注意が必要です。

具体的には、Webフォントなどを使用する場合は、かならず@font-faceを使わなければいけません。WebフォントをCDNFONTSなどから読み込もうとする場合、そのフォントの指定を他のスタイルと同じCSSファイルに書き込む必要があります。

この辺は冒頭にも書いたアプリのコミットログにありますので、こちらを参照してください。

https://github.com/side-beach-city/SBC.ImageMaker/commit/9fcc72ae30a323b360194f104242fded46af7d81

html2canvasじゃダメなんですか?

同種のことを行うライブラリにhtml2canvasというモジュールがあります。

http://html2canvas.hertzen.com/

ただこちらは独自にCSSを解釈して処理を行っているため、ブラウザと対応しているCSSが若干異なります(なにをサポートしていないかは、公式ドキュメントのfeaturesのページに書いてあります)。

今回は使おうとしているCSSにモロに非対応のプロパティがあったので使用できませんでした。

参考文献

Discussion