dom-to-image、html-to-image 和 html2canvas 都是用于将 HTML DOM 元素转换为图像(如 PNG 或 JPEG)的 JavaScript 库,适用于截图、导出报表、生成分享卡片等场景。它们通过读取 DOM 结构、样式和内容,在浏览器中渲染成位图,最终输出为 Base64 数据或 Blob 对象。尽管目标一致,但三者在实现机制、功能支持、兼容性和维护状态上存在显著差异。
在前端开发中,将网页内容转换为图像是一项常见需求 —— 无论是生成用户分享卡片、导出数据报表,还是保存可视化图表。dom-to-image、html-to-image 和 html2canvas 是三个主流方案,但它们的技术路径和适用场景大不相同。本文从实现原理、功能支持和实际使用角度进行深度对比。
dom-to-image 和 html-to-image 采用相似的思路:将目标 DOM 元素序列化为 SVG 字符串,嵌入 <foreignObject>,再将其绘制到 Canvas 上导出图像。
// dom-to-image / html-to-image 基本用法
import { toPng } from 'html-to-image'; // 或 'dom-to-image'
toPng(document.getElementById('capture'))
.then(dataUrl => {
const img = new Image();
img.src = dataUrl;
document.body.appendChild(img);
});
这种方式的优点是代码简洁,但受限于 <foreignObject> 的浏览器兼容性 —— 它无法正确渲染某些 CSS 属性(如 z-index、transform),且跨域图片会污染 Canvas,导致无法导出。
html2canvas 则完全不同:它遍历 DOM 树,逐节点解析样式,并在 Canvas 上手动重绘每个元素。
// html2canvas 基本用法
import html2canvas from 'html2canvas';
html2canvas(document.getElementById('capture')).then(canvas => {
document.body.appendChild(canvas);
});
这种“模拟渲染”方式虽更复杂,但能更好地还原现代 CSS 效果,如阴影、渐变、甚至部分伪元素。
dom-to-image:无法处理跨域图片。一旦包含,Canvas 被污染,调用 toDataURL() 会抛出安全错误。html-to-image:提供 skipFonts 和 useCORS 选项,可通过设置 img.crossOrigin = 'anonymous' 尝试加载跨域图片(前提是服务器允许)。// html-to-image 启用 CORS
toPng(node, { useCORS: true })
.then(dataUrl => console.log(dataUrl));
html2canvas:同样支持 useCORS: true,但需确保图片服务器返回 Access-Control-Allow-Origin 头。// html2canvas 启用 CORS
html2canvas(node, { useCORS: true })
.then(canvas => document.body.appendChild(canvas));
⚠️ 注意:即使启用 CORS,若图片未正确配置响应头,仍会失败。这是浏览器安全限制,非库本身问题。
dom-to-image:对 Flexbox、Grid、transform、box-shadow 等支持极差,常出现布局错乱。html-to-image:改进了部分 CSS 解析,能较好处理 Flex 容器和简单 Grid,但仍不支持 transform 缩放/旋转。html2canvas:支持 transform、border-radius、box-shadow、线性/径向渐变,甚至能渲染部分 ::before/::after 伪元素(需设置 allowTaint: true 并承担污染风险)。根据 npm 和 GitHub 信息:
dom-to-image 已被作者明确标记为 不再维护("This project is no longer maintained")。最后提交在多年前,存在已知 bug 且无修复计划。新项目应避免使用。html-to-image 是社区主导的活跃项目,定期发布更新,修复渲染问题,并保持与现代浏览器兼容。html2canvas 由独立开发者维护,更新频率中等,但文档完善,社区问题响应及时。html-to-image 足够快且轻量,API 简洁。html2canvas 虽慢一些,但视觉保真度更高。若你正在使用 dom-to-image,迁移到 html-to-image 通常只需替换包名和导入路径,因为 API 几乎一致:
- import * as domtoimage from 'dom-to-image';
+ import * as domtoimage from 'html-to-image';
domtoimage.toPng(node).then(...);
而从 html2canvas 迁移则需重写逻辑,因其返回 Canvas 而非 Data URL。
| 场景 | 推荐方案 |
|---|---|
| 新项目,需稳定维护 | html-to-image |
| 高保真渲染复杂 CSS | html2canvas |
| 旧项目临时维持 | dom-to-image(仅限过渡) |
核心原则:优先选择活跃维护的库;若视觉准确性至关重要,接受 html2canvas 的性能代价;若追求简洁和快速集成,html-to-image 是更平衡的选择。
dom-to-image 已被官方标记为不再维护,不应在新项目中使用。虽然它曾提供简洁的 API 和基于 SVG 的渲染方式,但缺乏对现代 CSS 特性(如 Flexbox、Grid、自定义字体)的完整支持,且无法处理跨域图片或复杂布局。若维护旧项目且无升级计划,可临时沿用,但强烈建议迁移到活跃维护的替代方案。
选择 html-to-image 如果你需要一个现代化、活跃维护且兼容性良好的 DOM 转图像方案。它是 dom-to-image 的社区继任者,修复了大量渲染缺陷,支持更多 CSS 属性(包括部分 Flexbox 和 Grid),并能正确处理跨域图片(通过代理或 canvas 污染规避)。其 API 与 dom-to-image 高度兼容,迁移成本低,适合需要稳定输出和持续更新的生产环境。
选择 html2canvas 如果你处理的是高度动态或复杂样式的页面,并且能接受稍重的运行时开销。它不依赖 SVG 快照,而是通过递归解析 DOM 并在 Canvas 上重绘,因此对现代 CSS 支持更全面(包括 transform、box-shadow、伪元素等)。但需注意其对 CORS 图片的限制,以及在大型 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