导出d3+canvas绘制的图表为一张图片

32 阅读1分钟

写一个方法,实现vue导出d3+canvas绘制的图表为一张图片,改方法入参为 d3的dom的id和canvas的id

// 下载D3生成的SVG图表
function downloadD3Chart(svgSelector, fileName = 'd3-chart') {
  // 获取SVG元素
  const svg = document.querySelector(svgSelector);
  
  // 克隆SVG以避免修改原始内容
  const clone = svg.cloneNode(true);
  
  // 处理样式(将外部样式转换为内联样式)
  const style = document.createElementNS("http://www.w3.org/2000/svg", "style");
  style.textContent = Array.from(document.styleSheets)
    .filter(sheet => !sheet.href || sheet.href.startsWith(window.location.origin))
    .map(sheet => Array.from(sheet.cssRules).map(rule => rule.cssText).join('\n'))
    .join('\n');
  clone.insertBefore(style, clone.firstChild);
  
  // 将SVG转换为字符串
  const svgData = new XMLSerializer().serializeToString(clone);
  
  // 创建Blob对象
  const blob = new Blob(['<?xml version="1.0" standalone="no"?>\r\n', svgData], {
    type: 'image/svg+xml;charset=utf-8'
  });
  
  // 创建图片对象加载SVG
  const img = new Image();
  const url = URL.createObjectURL(blob);
  
  img.onload = function() {
    // 创建Canvas绘制图片
    const canvas = document.createElement('canvas');
    canvas.width = svg.clientWidth;
    canvas.height = svg.clientHeight;
    const ctx = canvas.getContext('2d');
    ctx.fillStyle = '#ffffff'; // 设置白色背景
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.drawImage(img, 0, 0);
    
    // 创建下载链接
    const link = document.createElement('a');
    link.download = `${fileName}.png`;
    link.href = canvas.toDataURL('image/png');
    link.click();
    
    // 清理
    URL.revokeObjectURL(url);
  };
  
  img.src = url;
}

// 下载Canvas图表
function downloadCanvasChart(canvasSelector, fileName = 'canvas-chart') {
  const canvas = document.querySelector(canvasSelector);
  
  // 创建临时链接
  const link = document.createElement('a');
  link.download = `${fileName}.png`;
  
  // 对于已经绘制好的canvas,可以直接获取数据URL
  link.href = canvas.toDataURL('image/png');
  
  // 触发下载
  link.click();
}

// 组合下载(如果页面同时有D3和Canvas图表)
function downloadCombinedChart(svgSelector, canvasSelector, fileName = 'combined-chart') {
  const svg = document.querySelector(svgSelector);
  const canvas = document.querySelector(canvasSelector);
  
  // 创建新Canvas用于合并
  const combinedCanvas = document.createElement('canvas');
  combinedCanvas.width = Math.max(svg.clientWidth, canvas.width);
  combinedCanvas.height = svg.clientHeight + canvas.height;
  const ctx = combinedCanvas.getContext('2d');
  
  // 绘制背景
  ctx.fillStyle = '#ffffff';
  ctx.fillRect(0, 0, combinedCanvas.width, combinedCanvas.height);
  
  // 先处理SVG
  const svgData = new XMLSerializer().serializeToString(svg);
  const svgBlob = new Blob(['<?xml version="1.0" standalone="no"?>\r\n', svgData], {
    type: 'image/svg+xml;charset=utf-8'
  });
  const svgUrl = URL.createObjectURL(svgBlob);
  
  const img = new Image();
  img.onload = function() {
    // 绘制SVG内容
    ctx.drawImage(img, 0, 0);
    
    // 绘制Canvas内容
    ctx.drawImage(canvas, 0, svg.clientHeight);
    
    // 下载合并后的图片
    const link = document.createElement('a');
    link.download = `${fileName}.png`;
    link.href = combinedCanvas.toDataURL('image/png');
    link.click();
    
    URL.revokeObjectURL(svgUrl);
  };
  
  img.src = svgUrl;
}