ブラウザの画面を撮影して、画像として扱いたいことがありますよね。
たとえば、
Webで入稿してOGP用の画像を自動で生成するんだけど、サーバーでChromium動かすのはコストが高いし、vercel/satoriはレイアウトがイメージ通りにならないことがあるし...
ブラウザで実際に画面に表示させたものを画像にしてアップロードできたら 調整もしやすいのに
など。
そういうケースのためのライブラリがあります。
(参考: ダウンロード数比較 npmtrends.com)
@zumer/snapdom の中身を読み解く
どのようにして、画像が作成されているのでしょうか。
最も後発で最も活発に開発がされている @zumer/snapdom のコードを読んでみましょう。
各種インターフェースはこのファイルにあるようです。
コードを読み進めると、実際にキャプチャを行っていそうな関数をみつけられます。
captureDOM 関数は200行以上あるので末尾から追うと、
SVG画像のDataURLを返していることがわかりました。
つまり、入力されたDOM要素は何かしらの形でSVGに変換されていることになります。
svgHeader と svgFooter にはサイズが指定されている以上の情報はなさそうで、本体は foString という文字列だと推測できます。
foString は fo というXMLオブジェクトをシリアライズしたもののようです。
さらに遡ると、 const fo = document.createElementNS(svgNS, 'foreignObject') という記述があり、foreignObjectというSVGの要素であることがわかります。
foreignObjectとは何か、MDNを読むと、
異なる XML 名前空間の要素を含みます。ブラウザーのコンテキストでは、ほとんどの場合 (X)HTML です。
とあり、XMLにHTMLを含めることができるようです。
実際に、style要素とdiv要素が fo に挿入されています。
div要素には state.clone が挿入されていますが、関数前半を読むと、引数のelementを prepareClone 関数でクローンしていそうなことがわかります。
このように foreignObject を活用し、HTML(DOM)要素を含むSVGを作成、SVGのDataURLをimg要素やcanvas要素に描画することでHTML(DOM)が画像化されているようです。
おわり
HTML(DOM)要素は擬似的に撮影できます。
SVGにforeignObject経由でHTMLを含め、そのSVG画像を描画すればいいということです。
今回は省略しましたが、実際には複雑なスタイル抽出処理が挟まるので簡単には真似ができないですが、仕組みがわかると面白いですね。
ZOZO社内で実際に利用されているシーンもあります。併せて読んでみてください。
