前端元素转图片,dom-to-image-more入门教程

0 阅读6分钟

点赞 + 关注 + 收藏 = 学会了

在前端开发中,将 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 无法加载跨域资源。

解决:

  1. 图片服务器配置 CORS 跨域(OSS/CDN 后台开启);
  2. 给 img 标签添加 crossorigin="anonymous"
  3. **兜底方案:**将图片转为 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,截图后再缩小,或者手动指定 widthheight 为原始尺寸的 2 倍。

6. Vue3 中获取不到 DOM(报错 null)

现象: 调用截图时,ref.valuenull,控制台报错:Cannot read properties of null。Vue3 是异步渲染,DOM 还未挂载就执行了截图方法。

**解决:**用 onMountednextTick 保证 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 样式丢失 / 不兼容

现象:

  • 阴影、渐变、filtertransform 样式失效;
  • 圆角、边框样式异常;
  • 伪元素(::before/::after)完全不显示。

dom-to-image 基于 SVG 实现,不支持部分高级 CSS 属性;伪元素是 CSS 生成,无真实 DOM 实体。

解决:

  • 替换兼容样式:用纯色替代复杂渐变,用 box-shadow 替代复杂滤镜;

  • 伪元素改为真实 DOM(最有效);

  • 避免使用:mix-blend-modeclip-pathposition: sticky 等不兼容属性。

8. iframe 内容无法截取

**现象:**iframe 区域截图后完全空白。

  • 跨域 iframe 浏览器禁止访问;

  • 同域 iframe 需手动处理内容。

解决:

  • 跨域 iframe:无完美解决方案(浏览器安全限制);
  • 同域 iframe:手动获取 iframe 内容拼接后截图。

以上就是本文的全部内容了。

点赞 + 关注 + 收藏 = 学会了