5
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

DOMをそのまま画像化する 〜html2canvas と dom-to-image の比較と他の方法〜

Last updated at Posted at 2024-09-25

HTMLのDOMをそのまま画像化したいと思ったことはありませんか?

ネット上で色々な情報がありますが、いわゆる「そのままの状態でスクショしたい」に答えるのは意外と難しいです。

主な選択肢と特徴

  • html2canvas
    • ユーザーにブラウザ上でダウンロードしてもらいたい場合におすすめです。ios, safariでも使えます
  • dom-to-image
    • ユーザーが特定のブラウザのみでダウンロードできればいい時におすすめです。safariでは正常に動かないことがあります
  • スクレイピングによる方法
    • ユーザーがダウンロードしなくても良く自分で特定のサイトからダウンロードする場合におすすめです
  • ブラウザの開発者ツール
    • 要素単位でのダウンロードが実装なしでできます
  • ブラウザの拡張機能
    • 拡張機能によりますが、ページ全体を綺麗にダウンロードするのには向いています

何にどの選択肢を使ったか

オリジナルのカードを作れるシステムで、ユーザーが入力したテキストや画像を元にカード画像を作る時に利用しました。

ios, safari では html2canvas, それ以外では dom-to-image を採用しました。

以下が主な理由です。

  • dom-to-image では ios, safari で正常に動かない
  • html2canvas では 縦書き(writing-mode: vertical-rl)が使えない

それぞれの詳細

html2canvas

html2canvas は、その名の通り、HTMLの要素をHTML canvas要素に変換するライブラリです。

Usage(GitHubより)

html2canvas(document.body).then(function(canvas) {
    document.body.appendChild(canvas);
});

append せずに HTMLCanvasElement.toDataURLを使って
HTMLCanvasElement.toDataURL("image/png") のようにすればpngなどの画像でダウンロードできます。

特徴

  • HTMLやCSSでの描画を CanvasRenderingContext2D で自力で1から再現したライブラリです
    • GitHubのコードを見ると、domをコピーして子要素をスタックして、下の方に描画される要素から1つ1つcanvasに描画しているのがわかります
  • 環境の影響をあまり受けず、safariやios端末でもほぼ同じ動作をします
  • writing-mode など、再現されていないプロパティがあります(今後の拡大に期待)
  • 同一ドメインの要素しか描画できません(外部の画像などは描画できないので注意)

dom-to-image

dom-to-image は、domを指定した拡張子の画像に変換するライブラリです。

Usage(GitHubより)

var node = document.getElementById('my-node');

domtoimage.toPng(node)
    .then(function (dataUrl) {
        var img = new Image();
        img.src = dataUrl;
        document.body.appendChild(img);
    })
    .catch(function (error) {
        console.error('oops, something went wrong!', error);
    });

特徴

  • DOMを XMLSerializer().serializeToString()MDNでの説明)でSVGにして、そこからCanvasなどを経て様々な画像に変換するライブラリです
    • 以下の「実装の主要部分」を参照
  • XMLSerializer に依存し、ios, safariでは text-shadow がうまく再現できなかったり、画像がうまく描画されない時があったりと不安定でした
  • 同一ドメインの要素しか描画できません(外部の画像などは描画できないので注意)

実装の主要部分

src/dom-to-image.js
function makeSvgDataUri(node, width, height) {
        return Promise.resolve(node)
            .then(function (node) {
                node.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
                return new XMLSerializer().serializeToString(node);
            })
            .then(util.escapeXhtml)
            .then(function (xhtml) {
                return '<foreignObject x="0" y="0" width="100%" height="100%">' + xhtml + '</foreignObject>';
            })
            .then(function (foreignObject) {
                return '<svg xmlns="http://www.w3.org/2000/svg" width="' + width + '" height="' + height + '">' +
                    foreignObject + '</svg>';
            })
            .then(function (svg) {
                return 'data:image/svg+xml;charset=utf-8,' + svg;
            });
    }

スクレイピングによる方法

PuppeterSelenium などのスクレイピングツールでは、スクリーンショットを撮ることができます。

例えば Puppeter では以下のメソッドが使えるので、

このように実装できます。

const puppeteer = require('puppeteer');

const main = async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  
  await page.goto('https://qiita.com/zuisho-1848');
  await page.screenshot({ path: 'example.png' });
  
  await browser.close();
}

main();

あまり使っていないので解説は他の記事に任せます。
例えば、以下の記事など参考になるかもしれません。

定期的にサイトを自動で訪れてスクショを撮りたいなど、人間が見ずにスクショを撮るのに便利だと思います。

(もしかしたら、Google の PageSpeed Insights で表示されるスクショはこれで撮ってるのかもしれないと思っています)

ブラウザの開発者ツール

開発者ツールの Element から画像化したいノードをサブクリックし、 Caputure node screenshot を押すとpngで保存できます。

スクリーンショット 2024-09-25 22.32.10.png

safariなどでも撮影できるようです。
以下の記事を引用しておきます。

レスポンシブの画面にして、右上の下矢印マークからも撮れます。(Caputure full size screenshot だとページ全体を撮れます)
スクリーンショット 2024-09-25 22.36.10.png

特徴

  • 外部のライブラリに頼らなくて良いです
  • 非エンジニアでも実装せずにパッと使えます
  • 再現度はあまり試せていないのでなんとも言えません
  • スマホからはできないので、一般のユーザーにやってもらうのは難しいです

ブラウザの拡張機能

ページ全体の画像を綺麗に撮りたい時に役立つ拡張機能があります。

使ったことがあるのは、以下のChromeの拡張機能です。
スクショというよりも、ページ全体を印刷しなければいけない時に、cssなどを綺麗に反映したい時に使いました(通常のページの印刷だとレスポンシブなどがひどいことになるので。。)

他にもあると思うので、探してみてください。

まとめ

HTML DOM の画像化は意外とシンプルにはいかないようで、ライブラリも結構強引にやっているものばかりです。
いつか、ブラウザのAPIなどで対応するといいなと思っています。
HTMLElement.toImage() みたいなメソッドがあればいいのに。)

ブラウザ開発者からしたら難しいよって話なんですかね?
「レンダリングする代わりにその結果を画像化する」みたいな。
専門外なのでこの程度にしておきます。

ちょっと宣伝

Hi!story【ハイスト】チーム(株式会社Highsto)ではメンバーを募集しています。

もし興味があったら覗いてみてください!

Xのフォローもよろしくです!

5
8
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
5
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?