HTMLとしてレンダリング・レイアウトした画像をPNGとして保存する
先日こんなツールを作成しました。
わたしが今まで配信しているポッドキャスト番組SBCast.では公開後に、その情報をInstagramにQRコード付きで紹介&YouTubeに音声+画像の動画をアップロードしていたのですが、それらをいちいちPC上のツールを使うことなくWeb上で作成できるツールです。
画像のレイアウトなどはCSSを使えば比較的簡単にできますので、それを画像として保存します。
結論
結論から言うと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ファイルに書き込む必要があります。
この辺は冒頭にも書いたアプリのコミットログにありますので、こちらを参照してください。
html2canvasじゃダメなんですか?
同種のことを行うライブラリにhtml2canvasというモジュールがあります。
ただこちらは独自にCSSを解釈して処理を行っているため、ブラウザと対応しているCSSが若干異なります(なにをサポートしていないかは、公式ドキュメントのfeaturesのページに書いてあります)。
今回は使おうとしているCSSにモロに非対応のプロパティがあったので使用できませんでした。
参考文献
- dom-to-imageを試す - Qiita
- html2canvasを試す - Qiita
- CSSで背景を透過させる方法3つを徹底解説 - WEBCAMP MEDIA(上記内容とは関係ないですが、今回のツールを作成する際に参照しました)
- html要素を<canvas>に描画する | totorajの開発日記
Discussion