图片导出

前端实现图片导出的几种方式

问题

在前端如何将 Canvas、DOM 元素或 SVG 导出为图片并下载?

解答

1. Canvas 导出图片

// 获取 canvas 元素
const canvas = document.getElementById('myCanvas');

// 方式一:toDataURL - 返回 base64 字符串
const dataURL = canvas.toDataURL('image/png'); // 默认 png
const jpegURL = canvas.toDataURL('image/jpeg', 0.8); // jpeg,质量 0.8

// 方式二:toBlob - 返回 Blob 对象(推荐,性能更好)
canvas.toBlob((blob) => {
  const url = URL.createObjectURL(blob);
  downloadImage(url, 'image.png');
  URL.revokeObjectURL(url); // 释放内存
}, 'image/png');

// 通用下载函数
function downloadImage(url, filename) {
  const link = document.createElement('a');
  link.href = url;
  link.download = filename;
  link.click();
}

2. DOM 元素导出图片

使用 html2canvas 库将任意 DOM 元素转为图片:

import html2canvas from 'html2canvas';

async function exportDOMToImage(element, filename = 'export.png') {
  // 将 DOM 转为 canvas
  const canvas = await html2canvas(element, {
    useCORS: true,        // 允许跨域图片
    scale: 2,             // 提高清晰度
    backgroundColor: '#fff'
  });
  
  // 导出下载
  canvas.toBlob((blob) => {
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = filename;
    link.click();
    URL.revokeObjectURL(url);
  });
}

// 使用
const element = document.getElementById('export-area');
exportDOMToImage(element, 'screenshot.png');

3. SVG 导出图片

function svgToImage(svgElement, filename = 'image.png') {
  // 序列化 SVG
  const svgData = new XMLSerializer().serializeToString(svgElement);
  const svgBlob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' });
  const url = URL.createObjectURL(svgBlob);
  
  // 创建 Image 加载 SVG
  const img = new Image();
  img.onload = () => {
    // 绘制到 canvas
    const canvas = document.createElement('canvas');
    canvas.width = svgElement.width.baseVal.value || 300;
    canvas.height = svgElement.height.baseVal.value || 150;
    
    const ctx = canvas.getContext('2d');
    ctx.drawImage(img, 0, 0);
    
    // 导出
    canvas.toBlob((blob) => {
      const downloadUrl = URL.createObjectURL(blob);
      const link = document.createElement('a');
      link.href = downloadUrl;
      link.download = filename;
      link.click();
      URL.revokeObjectURL(downloadUrl);
    });
    
    URL.revokeObjectURL(url);
  };
  img.src = url;
}

4. 封装通用导出工具

const ImageExporter = {
  // 下载 base64 图片
  downloadBase64(dataURL, filename) {
    const link = document.createElement('a');
    link.href = dataURL;
    link.download = filename;
    link.click();
  },
  
  // 下载 Blob
  downloadBlob(blob, filename) {
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = filename;
    link.click();
    URL.revokeObjectURL(url);
  },
  
  // Canvas 导出
  fromCanvas(canvas, filename, type = 'image/png', quality = 1) {
    return new Promise((resolve) => {
      canvas.toBlob((blob) => {
        this.downloadBlob(blob, filename);
        resolve(blob);
      }, type, quality);
    });
  },
  
  // 远程图片下载(需处理跨域)
  async fromURL(imageUrl, filename) {
    const response = await fetch(imageUrl);
    const blob = await response.blob();
    this.downloadBlob(blob, filename);
  }
};

// 使用示例
ImageExporter.fromCanvas(canvas, 'export.png');
ImageExporter.fromURL('https://example.com/image.jpg', 'download.jpg');

关键点

  • toDataURL 返回 base64 字符串,toBlob 返回 Blob 对象,后者内存效率更高
  • 导出后用 URL.revokeObjectURL() 释放内存
  • 跨域图片需要服务端配置 CORS,Canvas 中设置 crossOrigin="anonymous"
  • html2canvas 有局限性,部分 CSS 属性不支持(如 box-shadow 渲染可能有差异)
  • 提高导出清晰度可增大 scale 或使用 devicePixelRatio