dom-to-image 实战指南:把任意 DOM 节点转成 PNG、JPEG、SVG 图片,原理与踩坑一次讲清
【免费下载链接】dom-to-imageGenerates an image from a DOM node using HTML5 canvas项目地址: https://gitcode.com/gh_mirrors/do/dom-to-image
dom-to-image 是一个纯 JavaScript 的 DOM 截图库:传给它任意一个 DOM 节点,它在浏览器内把节点渲染成 SVG 矢量图或 PNG / JPEG 位图,全程无需服务端参与。核心思路是把克隆后的 HTML 塞进 SVG 的<foreignObject>再"拍照",因此 web 字体、<img>与背景图都会被自动内联进结果。它适合做报表卡片导出、社交分享图、局部缩略图这类"网页转图片"的需求,当前版本 2.6.0(见 package.json),运行时零依赖,MIT 协议。
三分钟安装并拿到第一张图 📸
三种引入方式并列给出,按需取一种:
npm install dom-to-image # 包管理器:npm / bower install dom-to-imageimport domtoimage from 'dom-to-image'; // ES6 // ES5 / CommonJS:var domtoimage = require('dom-to-image');<!-- 或 Bower 装完后直接引脚本,得到全局变量 domtoimage --> <script src="path/to/dom-to-image.min.js"></script>压缩版
dist/dom-to-image.min.js由 Grunt 的 uglify 任务生成(见 Gruntfile.js),引 src/dom-to-image.js 或 min 版均可。源码里typeof module !== 'undefined'时走module.exports,否则挂到全局,所以模块与<script>两种用法天然共存。
最简用法——所有顶层函数签名都是fn(node, options)并返回 Promise:
var node = document.getElementById('my-node'); domtoimage.toPng(node) .then(function (dataUrl) { // dataUrl 形如 data:image/png;base64,... var img = new Image(); img.src = dataUrl; document.body.appendChild(img); }) .catch(function (error) { console.error('渲染失败:', error); });拿到 data URL 后塞进<img src>就能立刻看到"页面上那个节点"的位图副本,这就是整条链路的最短路径。
按场景挑函数:贴页面、下载、压缩、过滤、取像素
把结果贴回页面——toPng(node, options)返回 PNG data URL;toSvg(node, options)返回 SVG data URL(矢量、无损、体积小,适合后续二次编辑)。
触发浏览器下载——toBlob(node, options)返回 PNG Blob,比 data URL 更适合喂给下载 API,内部优先走canvas.toBlob,不支持时回退到atob+Uint8Array手工构造:
domtoimage.toBlob(document.getElementById('my-node')) .then(function (blob) { window.saveAs(blob, 'my-node.png'); // FileSaver.js });压出更小的 JPEG——toJpeg专吃quality(0~1),直接透传给canvas.toDataURL('image/jpeg', quality),其余函数传了也无效:
domtoimage.toJpeg(node, { quality: 0.92 }) .then(function (dataUrl) { var link = document.createElement('a'); link.download = 'report-card.jpeg'; link.href = dataUrl; link.click(); });截图时剪掉不要的节点——filter返回false即整棵子树被丢弃,注意它不会对根节点调用,所以"filter 全 false"也照样渲染根节点:
domtoimage.toSvg(node, { filter: function (n) { return n.tagName !== 'SCRIPT'; } });拿原始像素做后处理——toPixelData返回Uint8Array,每 4 个字节是一个像素的 RGBA(0~255),按行主序排布,偏移量为(y * scrollWidth + x) * 4,适合喂给 OCR、做热力图或像素级 diff:
domtoimage.toPixelData(node) .then(function (pixels) { var x = 10, y = 20; var offset = (y * node.scrollWidth + x) * 4; var rgba = pixels.slice(offset, offset + 4); // Uint8Array[4] });
domtoimage.impl下的util、inliner、fontFaces、images等不是公开 API,只为单元测试暴露(spec/dom-to-image.spec.js 里能直接看到调用),业务代码请只用上面五个顶层函数。
渲染参数速查:七个选项一次看全
| 参数 | 类型 | 默认值 | 作用 | 生效位置 |
|---|---|---|---|---|
filter | Function | 无 | 返回true保留该节点;排除即连同子树一起删;不作用于根节点 | 克隆阶段 |
bgcolor | String | 无 | 背景色,任意合法 CSS 颜色值 | 克隆节点style.backgroundColor,且离屏 canvas 绘制前先fillRect铺底色 |
width/height | Number | 无 | 渲染前强加到克隆节点的宽高(px),同时决定 SVG 画布与 canvas 尺寸;缺省时取scrollWidth/Height加边框宽 | applyOptions与画布创建 |
style | Object | 无 | 逐属性拷贝到克隆节点 style,属性名用 JS 命名(如backgroundColor) | applyOptions |
quality | Number | 1.0 | JPEG 压缩质量(如0.92即 92%),仅toJpeg有效 | canvas.toDataURL |
cacheBust | Boolean | false | 资源请求 URL 追加当前时间戳查询串,绕开缓存导致的 CORS 问题 | 资源下载getAndEncode |
imagePlaceholder | String | undefined | 图片拉取失败(含 30 秒超时)时填入的 data URL 占位图;不设则记错误并留空,行为偏 fail-fast | 资源下载getAndEncode |
width/height与style还可以组合出"缩放导出":先给克隆节点定尺寸,再注入transform: scale(...)(spec/dom-to-image.spec.js 的scale用例就是这么测的)。
它到底怎么把 DOM 变图的:foreignObject 四步走 ⚙️
README.md 的 "How it works" 列了八步,读源码后可以压缩成一条主干:
- 递归克隆 + 抄计算样式:
cloneNode按子节点顺序逐个深克隆,每个元素用window.getComputedStyle取计算样式拷进副本——Chrome 走cssText一步到位,Firefox 回退到逐属性setProperty。::before/::after伪元素不会被克隆,于是库为每个有content的伪元素生成随机 class,追加一个内联<style>块重建它;<textarea>/<input>的用户输入值也会被显式同步到副本上。 - 字体与图片全部内联:
fontFaces遍历document.styleSheets找出所有@font-face规则,下载字体文件、base64 编码后拼成一个<style>挂到克隆节点;images模块用同样手段改写所有<img src>和 CSSbackground里的url(...)。底层是XMLHttpRequest(responseType: 'blob')+FileReader.readAsDataURL,MIME 类型按 URL 扩展名查表推断。目的是让最终 SVG 完全自包含,栅格化时不再依赖任何外部请求。 - 序列化进 foreignObject:
new XMLSerializer().serializeToString(clone)把节点转成 XHTML,包一层<foreignObject>再包<svg width height>,产出data:image/svg+xml;charset=utf-8,...。这一步有个容易被忽略的细节——#会被转义成%23、换行转成%0A(escapeXhtml),否则#fff这类颜色会截断 data URL。 - 画布出图(可选):
toPng/toJpeg/toBlob/toPixelData再往前一步——用 SVG data URL 构造Image,等约 100ms 让浏览器完成 foreignObject 渲染,然后drawImage到新建的 canvas 上,最后分别toDataURL/toBlob/getImageData。toSvg则停在第 3 步,data URL 本身就是交付物。
一个隐藏分支:节点里若含<canvas>,克隆时会先toDataURL()把画布内容转成图片再塞进去——这也是 tainted canvas 翻车的入口,见下一节。
兼容性与边界:这些情况会翻车 ⚠️
- 浏览器:README 撰写时验证于最新 Chrome 与 Firefox(49 / 45);Chrome 处理大型 DOM 树明显更快(得益于
cssStyleDeclaration.cssText支持)。IE 明确不支持且不会支持(无<foreignObject>);Safari 也不支持(更严格的 foreignObject 安全模型),官方建议的绕行方案是toSvg拿到 SVG 后放服务端栅格化。 - 硬性前提:浏览器需支持
Promise与 SVG<foreignObject>,其余只用了标准 Web API。 - tainted canvas:待渲染节点里若有被跨域图片污染的
<canvas>,克隆期调用toDataURL会直接抛 SecurityError,渲染基本失败。 - 外部样式表异常:Firefox 曾对部分外部样式表读取报错(上游 issue #13);源码里
getCssRules用 try/catch 兜底,只打日志不中断渲染,spec/dom-to-image.spec.js 有对应"加载出错不崩溃"的回归用例。 - 资源失败策略:图片/字体拉取超时阈值 30 秒;是否用
imagePlaceholder顶替、还是记错误日志后留空,完全由该参数决定。 - 内嵌 SVG 修复:克隆出的
SVGElement会补xmlns命名空间,<rect>的 width/height 属性会转写成样式属性(fixSvg),否则序列化成 data URL 后图形会丢失。 - 伪元素:只有
content非空(非''/none)的::before/::after才会被重建,纯装饰性空 content 不保。 - 工具链与授权(一句话带过):Grunt + Karma(Chrome/Firefox 双端)+ Mocha/Chai 跑测试,
js-imagediff做控制图像素对比、Tesseract OCR 校验文本是否真的渲染出来;npm test等价于grunt test,默认任务另加 jshint 与 uglify。想本地复现可git clone https://gitcode.com/gh_mirrors/do/dom-to-image后装依赖运行。作者 Anatolii Saienko,原始创意来自 Paul Bakaus 的 domvas,MIT 授权。
收尾:谁适合、谁不适合
一句话总结:dom-to-image 用"SVG foreignObject 装 HTML + canvas 拍照"的纯前端管线,把任意 DOM 节点变成可下载、可嵌入、可逐像素读取的图片,代价是要接受宿主浏览器带来的渲染差异与一堆边界条件。
适合:Chromium 系浏览器为主的业务(报表/图表/卡片导出、分享图、缩略图生成);无服务端截图条件的前端项目;需要原始像素数据做 OCR、diff 的后处理场景。
不适合:必须覆盖 IE 或 Safari 客户端的截图需求(只能服务端兜底);节点内含 tainted canvas;要求跨浏览器"逐像素 100% 一致"的极端保真场景——它复现的是"当前浏览器渲染的结果",不是设计稿的绝对真值。
【免费下载链接】dom-to-imageGenerates an image from a DOM node using HTML5 canvas项目地址: https://gitcode.com/gh_mirrors/do/dom-to-image
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考