dom-to-image vs html-to-image vs html2canvas
Web 页面转图像的前端方案对比
dom-to-imagehtml-to-imagehtml2canvas类似的npm包:

Web 页面转图像的前端方案对比

dom-to-imagehtml-to-imagehtml2canvas 都是用于将 HTML DOM 元素转换为图像(如 PNG 或 JPEG)的 JavaScript 库,适用于截图、导出报表、生成分享卡片等场景。它们通过读取 DOM 结构、样式和内容,在浏览器中渲染成位图,最终输出为 Base64 数据或 Blob 对象。尽管目标一致,但三者在实现机制、功能支持、兼容性和维护状态上存在显著差异。

npm下载趋势

3 年

GitHub Stars 排名

统计详情

npm包名称
下载量
Stars
大小
Issues
发布时间
License
dom-to-image010,776-3399 年前MIT
html-to-image07,113315 kB1971 年前MIT
html2canvas031,8383.38 MB1,050-MIT

Web 页面转图像:dom-to-image、html-to-image 与 html2canvas 深度对比

在前端开发中,将网页内容转换为图像是一项常见需求 —— 无论是生成用户分享卡片、导出数据报表,还是保存可视化图表。dom-to-imagehtml-to-imagehtml2canvas 是三个主流方案,但它们的技术路径和适用场景大不相同。本文从实现原理、功能支持和实际使用角度进行深度对比。

🛠️ 渲染机制:SVG 快照 vs Canvas 重绘

dom-to-imagehtml-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-indextransform),且跨域图片会污染 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:提供 skipFontsuseCORS 选项,可通过设置 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,若图片未正确配置响应头,仍会失败。这是浏览器安全限制,非库本身问题。

CSS 特性支持

  • dom-to-image:对 Flexbox、Grid、transformbox-shadow 等支持极差,常出现布局错乱。
  • html-to-image:改进了部分 CSS 解析,能较好处理 Flex 容器和简单 Grid,但仍不支持 transform 缩放/旋转。
  • html2canvas:支持 transformborder-radiusbox-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 简洁。
  • 复杂动态 UI(如仪表盘、含动画的图表)html2canvas 虽慢一些,但视觉保真度更高。
  • 含大量跨域资源的页面:三者均受限于浏览器 CORS 策略,需配合后端代理或使用同源图片。

🔄 迁移建议

若你正在使用 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
高保真渲染复杂 CSShtml2canvas
旧项目临时维持dom-to-image(仅限过渡)

核心原则:优先选择活跃维护的库;若视觉准确性至关重要,接受 html2canvas 的性能代价;若追求简洁和快速集成,html-to-image 是更平衡的选择。

如何选择: dom-to-image vs html-to-image vs html2canvas

  • dom-to-image:

    dom-to-image 已被官方标记为不再维护,不应在新项目中使用。虽然它曾提供简洁的 API 和基于 SVG 的渲染方式,但缺乏对现代 CSS 特性(如 Flexbox、Grid、自定义字体)的完整支持,且无法处理跨域图片或复杂布局。若维护旧项目且无升级计划,可临时沿用,但强烈建议迁移到活跃维护的替代方案。

  • html-to-image:

    选择 html-to-image 如果你需要一个现代化、活跃维护且兼容性良好的 DOM 转图像方案。它是 dom-to-image 的社区继任者,修复了大量渲染缺陷,支持更多 CSS 属性(包括部分 Flexbox 和 Grid),并能正确处理跨域图片(通过代理或 canvas 污染规避)。其 API 与 dom-to-image 高度兼容,迁移成本低,适合需要稳定输出和持续更新的生产环境。

  • html2canvas:

    选择 html2canvas 如果你处理的是高度动态或复杂样式的页面,并且能接受稍重的运行时开销。它不依赖 SVG 快照,而是通过递归解析 DOM 并在 Canvas 上重绘,因此对现代 CSS 支持更全面(包括 transform、box-shadow、伪元素等)。但需注意其对 CORS 图片的限制,以及在大型 DOM 树下的性能表现。适合对视觉保真度要求高、且能控制渲染上下文的场景。

dom-to-image的README

DOM to Image

Build Status

What is it

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.

Installation

NPM

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

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>

Usage

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.


Rendering options

filter

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.

bgcolor

A string value for the background color, any valid CSS color value.

height, width

Height and width in pixels to be applied to node before rendering.

style

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.

quality

A number between 0 and 1 indicating image quality (e.g. 0.92 => 92%) of the JPEG image. Defaults to 1.0 (100%)

cacheBust

Set to true to append the current time as a query string to URL requests to enable cache busting. Defaults to false

imagePlaceholder

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

Browsers

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.`

Dependencies

Source

Only standard lib is currently used, but make sure your browser supports:

Tests

Most 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

How it works

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:

  1. Clone the original DOM node recursively

  2. Compute the style for the node and each sub-node and copy it to corresponding clone

    • and don't forget to recreate pseudo-elements, as they are not cloned in any way, of course
  3. 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

  4. Embed images

    • embed image URLs in <img> elements

    • inline images used in background CSS property, in a fashion similar to fonts

  5. Serialize the cloned node to XML

  6. Wrap XML into the <foreignObject> tag, then into the SVG, then make it a data URL

  7. 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

  8. Done!

Things to watch out for

  • 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.

Authors

Anatolii Saienko, Paul Bakaus (original idea)

License

MIT