点赞 + 关注 + 收藏 = 学会了
在前端开发中,将 DOM 节点转换为图片是一个常见的需求(如生成海报、简历导出等)。
原版 dom-to-image 已于 2017 年停止维护,dom-to-image-more 是社区修复版,修复了许多原版的 Bug(特别是对 Chrome 高版本的兼容性)。
本教程将基于 Vue 3 (Composition API),带你从零开始掌握这个库。
环境准备与安装
首先,在你的项目中安装依赖:
npm install dom-to-image-more
# 或者
yarn add dom-to-image-more
基础用法
先写一个最简可运行示例:将页面中的 DOM 转换为 PNG 并自动下载。
在 Vue 3 中,通常使用 ref 来获取 DOM 元素。

<template>
<div>
<!-- 要截图的区域 -->
<div
ref="cardRef"
class="capture-area"
style="padding: 20px; background: #f0f0f0;"
>
<h2>这是2级标题</h2>
<img src="https://iili.io/fcR7gSe.md.png" alt="" style="width: 140px;">
<p style="font-size: 16px; margin: 0;">随便写点什么,dom-to-image-more 会把它变成图片~</p>
</div>
<!-- 截图按钮 -->
<button @click="downloadPng">点击截图</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
import domtoimage from 'dom-to-image-more';
// 获取要截图的 DOM 元素
const cardRef = ref(null);
const downloadPng = async () => {
try {
// 核心:调用 toPng 方法转换 DOM
const dataUrl = await domtoimage.toPng(cardRef.value);
// 创建a标签自动下载
const link = document.createElement('a');
link.download = 'dom截图.png'; // 文件名
link.href = dataUrl;
link.click();
} catch (error) {
console.error('截图失败:', error);
}
};
</script>
<style scoped>
.capture-area {
width: 400px;
box-sizing: border-box;
border: 1px solid red;
border-radius: 20px;
}
</style>
点击“点击截图”按钮后 dom-to-image-more 就开始工作了,它将 cardRef 里的元素都打包成 PNG 图片下载到本地。

导出图片的几种方法
dom-to-image-more 提供 4 个核心转换方法,均返回 Promise:
| 方法 | 作用 |
|---|---|
toPng(node, options) | 转 PNG 格式(Base64) |
toJpeg(node, options) | 转 JPEG 格式(Base64) |
toSvg(node, options) | 转 SVG 格式 |
toBlob(node, options) | 转 Blob 对象(用于上传服务器) |
需要注意的是,使用了 toXXX 方法后,下载图片时(link.download)配置的文件名后缀也要跟着改,比如导出 SVG:
// 省略部分代码
const downloadPng = async () => {
try {
// 核心:调用 toPng 方法转换 DOM
const dataUrl = await domtoimage.toSvg(cardRef.value);
// 创建a标签自动下载
const link = document.createElement('a');
link.download = 'dom截图.svg'; // 文件名
link.href = dataUrl;
link.click();
} catch (error) {
console.error('截图失败:', error);
}
};
这是下载后的SVG文件

基础配置项
toXXX 方法第一个参数是截图元素,第二个参数是配置项。
如果截图元素没配置背景色(backgroundColor),生成的 png 图片背景是透明的。
此时可以用 bgcolor 给截图设置一个默认背景色。

上图是截图后的效果。
<template>
<div>
<!-- 要截图的区域 -->
<div
ref="cardRef"
class="capture-area"
style="padding: 20px;"
>
<h2>这是2级标题</h2>
<img src="https://iili.io/fcR7gSe.md.png" alt="" style="width: 140px;">
<p style="font-size: 16px; margin: 0;">随便写点什么,dom-to-image-more 会把它变成图片~</p>
</div>
<!-- 截图按钮 -->
<button @click="downloadPng">点击截图</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import domtoimage from 'dom-to-image-more';
// 获取要截图的 DOM 元素
const cardRef = ref(null);
const downloadPng = async () => {
try {
// 核心:调用 toPng 方法转换 DOM
const dataUrl = await domtoimage.toPng(cardRef.value, {
bgcolor: '#ff0000'
});
// 创建a标签自动下载
const link = document.createElement('a');
link.download = 'dom截图.png'; // 文件名
link.href = dataUrl;
link.click();
} catch (error) {
console.error('截图失败:', error);
}
};
</script>
<style scoped>
.capture-area {
width: 400px;
box-sizing: border-box;
border: 1px solid red;
border-radius: 20px;
}
</style>
除了 bgcolor,还可以设置:
- quality:图片质量【0-1,仅JPEG生效】
- scale: 缩放比例。缩放比例决定了图片的清晰度,通常这么设置:
scale: window.devicePixelRatio || 2。 - filter:过滤器,可以排除特定的 DOM 节点不参与截图。
- width / height:强制指定生成的图片宽高。
- style:在截图时临时为 DOM 节点应用特定的 CSS 样式。
过滤不需要的节点
如果 DOM 中有按钮、水印等不需要截图的元素,用 filter 过滤:
// 省略部分代码
const options = {
filter: (node) => {
// 排除带有 class="no-screenshot" 的元素
return !node.classList?.contains('no-screenshot');
}
};
转换为 Blob 上传服务器
大部分场景需要将图片上传到后端,用 toBlob 替代 toPng:
// 上传图片到服务器
const uploadImage = async () => {
const blob = await domtoimage.toBlob(cardRef.value);
// 构造 FormData
const formData = new FormData();
formData.append('image', blob, 'screenshot.png');
// 调用接口上传
// await axios.post('/upload', formData);
};
避坑指南
dom-to-image-more 的原理是将 DOM 转换为 SVG 的 <foreignObject>,然后再渲染到 Canvas 上。由于安全策略和浏览器限制,以下是实战中必然会遇到的坑:
1. 跨域图片(CORS)
现象: 图片生成后,原本的图片位置是空白,或者控制台报错。 浏览器同源策略限制,SVG foreignObject 无法加载跨域资源。
解决:
- 图片服务器配置 CORS 跨域(OSS/CDN 后台开启);
- 给 img 标签添加
crossorigin="anonymous"; - **兜底方案:**将图片转为 Base64 再渲染。
<!-- 正确写法 -->
<img :src="imgUrl" crossorigin="anonymous" alt="跨域图片" />
2. 字体丢失问题
现象: 生成的图片中,自定义字体变成了系统默认字体(如宋体或 Arial)。
解决:
dom-to-image-more会尝试抓取@font-face声明。请确保你的字体文件也是跨域友好的。- 稳妥方案: 将字体转换为 Base64 直接写在 CSS 中。
3. Safari 兼容性大坑
现象: 在 Safari 浏览器下,图片经常出现偏移、部分元素不显示或空白。
解决:
-
这是因为 Safari 对 SVG 内部的
foreignObject渲染极其严格。 -
对策: 在生成前,手动触发一次重绘。
-
代码补丁:
// 连续调用两次,或者在 requestAnimationFrame 后执行 await domtoimage.toPng(el); const finalUrl = await domtoimage.toPng(el);
4. 滚动条与截断
现象: 如果 DOM 节点有滚动条,生成的图片可能只包含可见区域。
解决:
- 在生成图片前,通过
style参数将目标节点的高度强制设为scrollHeight。 - 或者在截图前将容器样式临时改为
overflow: visible。
const downloadLongScreenshot = async () => {
const dom = cardRef.value;
// 保存原始样式
const originalOverflow = dom.style.overflow;
const originalHeight = dom.style.height;
// 临时展开
dom.style.overflow = 'visible';
dom.style.height = 'auto';
// 截图
const dataUrl = await domtoimage.toPng(dom);
// 恢复样式
dom.style.overflow = originalOverflow;
dom.style.height = originalHeight;
};
5. 模糊与 Retina 屏
现象: 在高分辨率屏幕(如 MacBook)上生成的图片很模糊。
**解决:**该库会尝试处理 DPI,但最有效的方法是:先通过 CSS transform: scale(2) 放大 DOM,截图后再缩小,或者手动指定 width 和 height 为原始尺寸的 2 倍。
6. Vue3 中获取不到 DOM(报错 null)
现象: 调用截图时,ref.value 为 null,控制台报错:Cannot read properties of null。Vue3 是异步渲染,DOM 还未挂载就执行了截图方法。
**解决:**用 onMounted 或 nextTick 保证 DOM 已渲染:
import { ref, onMounted, nextTick } from 'vue';
const cardRef = ref(null);
// 方案1:挂载完成后执行
onMounted(() => {
// 可在这里执行初始化截图
});
// 方案2:点击时用 nextTick
const downloadPng = async () => {
await nextTick(); // 等待 DOM 更新
const dataUrl = await domtoimage.toPng(cardRef.value);
};
7. CSS 样式丢失 / 不兼容
现象:
- 阴影、渐变、
filter、transform样式失效; - 圆角、边框样式异常;
- 伪元素(
::before/::after)完全不显示。
dom-to-image 基于 SVG 实现,不支持部分高级 CSS 属性;伪元素是 CSS 生成,无真实 DOM 实体。
解决:
-
替换兼容样式:用纯色替代复杂渐变,用
box-shadow替代复杂滤镜; -
伪元素改为真实 DOM(最有效);
-
避免使用:
mix-blend-mode、clip-path、position: sticky等不兼容属性。
8. iframe 内容无法截取
**现象:**iframe 区域截图后完全空白。
-
跨域 iframe 浏览器禁止访问;
-
同域 iframe 需手动处理内容。
解决:
- 跨域 iframe:无完美解决方案(浏览器安全限制);
- 同域 iframe:手动获取 iframe 内容拼接后截图。
以上就是本文的全部内容了。
点赞 + 关注 + 收藏 = 学会了