dom-to-image、html-to-image、html2canvas はいずれもブラウザ上でHTML要素を画像(PNGやJPEGなど)に変換するためのJavaScriptライブラリです。これらはスクリーンショット機能、レポート生成、コンテンツ共有などのユースケースで広く使われます。dom-to-image と html-to-image はSVGベースのアプローチを採用し、CSSスタイルやフォントをより正確に再現します。一方、html2canvas はCanvas APIを直接利用して描画をシミュレートし、複雑なDOM構造にも対応しますが、一部のCSSプロパティや外部リソースの処理に制限があります。
フロントエンド開発で「画面の一部を画像として保存したい」という要件はよくあります。例えば、グラフの共有、レポート出力、ユーザー生成コンテンツのプレビューなどです。そのようなニーズを満たす代表的なnpmパッケージが dom-to-image、html-to-image、html2canvas です。これらは似た目的を持ちますが、内部実装、対応範囲、メンテナンス状況に大きな違いがあります。この記事では、実際のコード例を交えながら、各ライブラリの特性と選定基準を解説します。
まず最初に確認すべきは、パッケージが現在もアクティブにメンテナンスされているかです。
dom-to-image は npmページ および GitHubリポジトリ で明確に 非推奨(deprecated) とされています。作者は後継として html-to-image を推奨しています。新規プロジェクトでの使用は避けてください。
html-to-image は dom-to-image のフォークであり、GitHubリポジトリ は活発に更新されており、TypeScript定義やモダンなツールチェーンをサポートしています。
html2canvas は長年利用されてきた成熟したライブラリで、公式サイト も整備されており、定期的に更新されています。
💡 新規プロジェクトでは、
dom-to-imageは選択肢から外しましょう。
各ライブラリの基本的な使い方を比較します。対象は <div id="capture">Hello World</div> という要素とします。
dom-to-image(非推奨)import domtoimage from 'dom-to-image';
const node = document.getElementById('capture');
domtoimage.toPng(node)
.then(dataUrl => {
const img = new Image();
img.src = dataUrl;
document.body.appendChild(img);
});
html-to-imageimport { toPng } from 'html-to-image';
const node = document.getElementById('capture');
toPng(node)
.then(dataUrl => {
const img = new Image();
img.src = dataUrl;
document.body.appendChild(img);
});
html2canvasimport html2canvas from 'html2canvas';
const node = document.getElementById('capture');
html2canvas(node).then(canvas => {
const img = new Image();
img.src = canvas.toDataURL();
document.body.appendChild(img);
});
見た目は似ていますが、内部の処理は大きく異なります。
dom-to-image / html-to-image:SVG経由の描画これらのライブラリは、DOM要素を一時的にSVGに変換し、そのSVGをCanvasに描画して画像データURLを生成します。この方式の利点は、CSSスタイルやWebフォントを比較的正確に再現できることです。
// html-to-image の内部処理イメージ
// 1. DOM → SVG文字列(インラインスタイル付き)
// 2. SVGをBlob URLに変換
// 3. そのURLをImage要素で読み込み
// 4. Canvasに描画 → toDataURL()
ただし、iframe、動的Canvas、video要素などはサポートされません。また、CORSで保護された外部画像は描画されない可能性があります。
html2canvas:CanvasによるDOM再現html2canvas はブラウザのレンダリングエンジンを模倣し、Canvas APIを使ってDOMノードを1つずつ描画します。つまり、実際のスクリーンショットではなく「再現」 です。
この方式の利点は、iframe以外のほとんどのHTML要素を扱えることです。ただし、以下の制限があります:
box-shadow、transform、filter などの一部CSSプロパティが正しく描画されないhtml-to-image は、fontEmbedCss オプションでGoogle FontsなどのWebフォントを自動埋め込みます。
import { toPng } from 'html-to-image';
toPng(node, {
fontEmbedCss: true // デフォルトで有効
});
一方、html2canvas は useCORS: true オプションを指定することで、CORSヘッダー付きのフォント/画像を読み込もうとしますが、サーバー側の設定が必須です。
html2canvas(node, {
useCORS: true
});
clip-path)clip-path や mask といった高度なCSSプロパティは、html-to-image ではSVG互換のもののみ動作し、html2canvas では未対応です。どちらも完全な再現は難しいため、事前に検証が必要です。
html-to-image は toPng()、toJpeg()、toSvg()、toCanvas() を提供。JPEGでは品質調整可能。// JPEGで品質80%
toJpeg(node, { quality: 0.8 });
html2canvas は常にCanvasを返すため、toDataURL('image/jpeg', 0.8) で形式と品質を指定。html2canvas(node).then(canvas => {
const dataUrl = canvas.toDataURL('image/jpeg', 0.8);
});
html2canvas は ignoreElements オプションで特定の要素を除外できます。
html2canvas(node, {
ignoreElements: (el) => el.classList.contains('no-screenshot')
});
html-to-image は skipFonts や cacheBust などの細かい制御が可能ですが、要素単位の除外はできません。代わりに、一時的にDOMを操作してからキャプチャする必要があります。
html-to-image が高速で軽量。html2canvas がより安定。ただし、描画時間がかかる場合あり。html2canvas は scale オプションで解像度を調整でき、高DPIディスプレイ向けに高品質画像を生成可能です。
html2canvas(node, {
scale: 2 // 通常の2倍の解像度
});
html-to-imageimport { toPng } from 'html-to-image';
const download = async () => {
const dataUrl = await toPng(document.getElementById('card'));
const link = document.createElement('a');
link.download = 'card.png';
link.href = dataUrl;
link.click();
};
html-to-image はiframe非対応html2canvas(ただしiframe内は空白になる)toDataURL() で取得し、別途合成| 特徴 | dom-to-image | html-to-image | html2canvas |
|---|---|---|---|
| メンテナンス状況 | 非推奨 | アクティブ | アクティブ |
| 内部実装 | SVG → Canvas | SVG → Canvas | Canvas再現 |
| Webフォント対応 | △(手動対応必要) | ◯(自動埋め込み) | △(CORS必須) |
| iframe対応 | ✕ | ✕ | △(空白になる) |
| 複雑CSS対応 | △(SVG互換のみ) | △(SVG互換のみ) | ✕(多くのプロパティ未対応) |
| 大規模DOM | △ | △ | ◯ |
| 軽量さ | ◯ | ◯ | △(やや重め) |
html-to-image を試してください。シンプルで再現性が高く、TypeScript対応も万全です。html2canvas の挙動を検証しましょう。特に、CSSの制限を事前に把握することが重要です。dom-to-image は絶対に新規で使わないでください。既存コードがある場合も、可能なら html-to-image への移行を検討しましょう。どのライブラリを選ぶにせよ、実際のターゲット環境(ブラウザ、OS、DPR)で必ずテストすることを忘れないでください。画像の再現性は環境によって大きく変わるためです。
dom-to-image はシンプルなDOM要素の画像化に適していますが、公式には非推奨(deprecated)とされています。新しいプロジェクトでは使用せず、代わりに後継である html-to-image を検討してください。既存プロジェクトで安定稼働している場合は、移行コストを考慮しつつ保守運用を続ける選択肢もありますが、新規開発では避けるべきです。
html-to-image は dom-to-image の後継として開発されており、同じSVGベースのアプローチを維持しながら、TypeScriptサポートやモダンなビルド環境、バグ修正を提供しています。CSSスタイルやWebフォントの再現性が高く、軽量で使いやすいAPIを求める場合に最適です。ただし、iframeや動的なCanvas要素など、高度なレンダリングには対応していません。
html2canvas はCanvas APIを用いてDOMを描画するため、iframeや動的コンテンツ、複雑なレイアウトを含むページでも比較的高い忠実度でスクリーンショットを取得できます。ただし、CSSの一部(例:mix-blend-mode や clip-path)や外部フォントの読み込みに制限があり、結果がブラウザネイティブのスクリーンショットと異なる場合があります。大規模なDOMやアニメーション付き要素のキャプチャが必要な場合に有力な選択肢です。
dom-to-image is a library which can turn arbitrary DOM node into a vector (SVG) or raster (PNG or JPEG) image, written in JavaScript. It's based on domvas by Paul Bakaus and has been completely rewritten, with some bugs fixed and some new features (like web font and image support) added.
npm install dom-to-image
Then load
/* in ES 6 */
import domtoimage from 'dom-to-image';
/* in ES 5 */
var domtoimage = require('dom-to-image');
bower install dom-to-image
Include either src/dom-to-image.js or dist/dom-to-image.min.js in your page
and it will make the domtoimage variable available in the global scope.
<script src="path/to/dom-to-image.min.js" />
<script>
domtoimage.toPng(node)
//...
</script>
All the top level functions accept DOM node and rendering options,
and return promises, which are fulfilled with corresponding data URLs.
Get a PNG image base64-encoded data URL and display right away:
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);
});
Get a PNG image blob and download it (using FileSaver, for example):
domtoimage.toBlob(document.getElementById('my-node'))
.then(function (blob) {
window.saveAs(blob, 'my-node.png');
});
Save and download a compressed JPEG image:
domtoimage.toJpeg(document.getElementById('my-node'), { quality: 0.95 })
.then(function (dataUrl) {
var link = document.createElement('a');
link.download = 'my-image-name.jpeg';
link.href = dataUrl;
link.click();
});
Get an SVG data URL, but filter out all the <i> elements:
function filter (node) {
return (node.tagName !== 'i');
}
domtoimage.toSvg(document.getElementById('my-node'), {filter: filter})
.then(function (dataUrl) {
/* do something */
});
Get the raw pixel data as a Uint8Array with every 4 array elements representing the RGBA data of a pixel:
var node = document.getElementById('my-node');
domtoimage.toPixelData(node)
.then(function (pixels) {
for (var y = 0; y < node.scrollHeight; ++y) {
for (var x = 0; x < node.scrollWidth; ++x) {
pixelAtXYOffset = (4 * y * node.scrollHeight) + (4 * x);
/* pixelAtXY is a Uint8Array[4] containing RGBA values of the pixel at (x, y) in the range 0..255 */
pixelAtXY = pixels.slice(pixelAtXYOffset, pixelAtXYOffset + 4);
}
}
});
All the functions under impl are not public API and are exposed only
for unit testing.
A function taking DOM node as argument. Should return true if passed node should be included in the output (excluding node means excluding it's children as well). Not called on the root node.
A string value for the background color, any valid CSS color value.
Height and width in pixels to be applied to node before rendering.
An object whose properties to be copied to node's style before rendering. You might want to check this reference for JavaScript names of CSS properties.
A number between 0 and 1 indicating image quality (e.g. 0.92 => 92%) of the JPEG image. Defaults to 1.0 (100%)
Set to true to append the current time as a query string to URL requests to enable cache busting. Defaults to false
A data URL for a placeholder image that will be used when fetching an image fails. Defaults to undefined and will throw an error on failed images
It's tested on latest Chrome and Firefox (49 and 45 respectively at the time
of writing), with Chrome performing significantly better on big DOM trees,
possibly due to it's more performant SVG support, and the fact that it supports
CSSStyleDeclaration.cssText property.
Internet Explorer is not (and will not be) supported, as it does not support
SVG <foreignObject> tag
Safari is not supported, as it uses a stricter security model on <foreignObject> tag. Suggested workaround is to use toSvg and render on the server.`
Only standard lib is currently used, but make sure your browser supports:
<foreignObject> tagMost importantly, tests depend on:
js-imagediff, to compare rendered and control images
ocrad.js, for the parts when you can't compare images (due to the browser rendering differences) and just have to test whether the text is rendered
There might some day exist (or maybe already exists?) a simple and standard way of exporting parts of the HTML to image (and then this script can only serve as an evidence of all the hoops I had to jump through in order to get such obvious thing done) but I haven't found one so far.
This library uses a feature of SVG that allows having arbitrary HTML content
inside of the <foreignObject> tag. So, in order to render that DOM node
for you, following steps are taken:
Clone the original DOM node recursively
Compute the style for the node and each sub-node and copy it to corresponding clone
Embed web fonts
find all the @font-face declarations that might represent web fonts
parse file URLs, download corresponding files
base64-encode and inline content as data: URLs
concatenate all the processed CSS rules and put them into one <style>
element, then attach it to the clone
Embed images
embed image URLs in <img> elements
inline images used in background CSS property, in a fashion similar to
fonts
Serialize the cloned node to XML
Wrap XML into the <foreignObject> tag, then into the SVG, then make it a
data URL
Optionally, to get PNG content or raw pixel data as a Uint8Array, create an Image element with the SVG as a source, and render it on an off-screen canvas, that you have also created, then read the content from the canvas
Done!
if the DOM node you want to render includes a <canvas> element with
something drawn on it, it should be handled fine, unless the canvas is
tainted -
in this case rendering will rather not succeed.
at the time of writing, Firefox has a problem with some external stylesheets (see issue #13). In such case, the error will be caught and logged.
Anatolii Saienko, Paul Bakaus (original idea)
MIT