阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 图表导出与打印

图表导出与打印

作者:陈川 阅读数:1412人阅读 分类: ECharts

数据可视化中的图表导出与打印

ECharts提供了多种方式将图表导出为图片或PDF格式,并支持直接打印。这些功能对于数据分享、报告生成和离线分析非常实用。开发者可以通过简单的配置实现复杂的导出需求。

导出为图片

ECharts内置了图片导出功能,可以通过getDataURL()方法获取图表base64格式的URL:

// 获取图表实例
const chartInstance = echarts.init(document.getElementById('chart-container'));

// 导出为PNG
const pngDataUrl = chartInstance.getDataURL({
  type: 'png',
  pixelRatio: 2,  // 提高导出图片质量
  backgroundColor: '#fff'
});

// 创建下载链接
const link = document.createElement('a');
link.href = pngDataUrl;
link.download = 'chart-export.png';
link.click();

支持导出的图片格式包括:

  • png (默认)
  • jpeg
  • svg (需要浏览器支持)

批量导出多个图表

实际项目中经常需要同时导出多个关联图表:

// 假设页面上有多个图表容器
const chartContainers = [
  '#chart1', 
  '#chart2',
  '#chart3'
];

// 使用Promise.all处理多个图表导出
Promise.all(chartContainers.map(container => {
  const chart = echarts.init(document.querySelector(container));
  return chart.getDataURL({ type: 'png' });
})).then(urls => {
  urls.forEach((url, index) => {
    const a = document.createElement('a');
    a.href = url;
    a.download = `chart-${index+1}.png`;
    a.click();
  });
});

导出为PDF

虽然ECharts本身不直接支持PDF导出,但可以结合jsPDF等库实现:

import jsPDF from 'jspdf';

// 先获取图表图片数据
const chart = echarts.init(document.getElementById('chart'));
const imgData = chart.getDataURL({ 
  type: 'jpeg',
  quality: 0.95
});

// 创建PDF文档
const doc = new jsPDF();
doc.addImage(imgData, 'JPEG', 10, 10, 180, 100);
doc.save('chart-export.pdf');

打印图表

直接调用浏览器打印API可以打印图表:

function printChart() {
  const printWindow = window.open('', '_blank');
  const chartHtml = `
    <!DOCTYPE html>
    <html>
      <head>
        <title>Chart Print</title>
        <style>
          body { margin: 0; padding: 20px; }
          #print-chart { 
            width: 100%;
            height: 600px; 
          }
        </style>
      </head>
      <body>
        <div id="print-chart"></div>
        <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
        <script>
          const chart = echarts.init(document.getElementById('print-chart'));
          chart.setOption(${JSON.stringify(chart.getOption())});
          setTimeout(() => window.print(), 500);
        </script>
      </body>
    </html>
  `;
  
  printWindow.document.write(chartHtml);
  printWindow.document.close();
}

高级导出配置

ECharts允许对导出过程进行精细控制:

// 自定义导出内容
chartInstance.getDataURL({
  type: 'png',
  excludeComponents: ['toolbox', 'dataZoom'], // 排除特定组件
  pixelRatio: 3, // 高清导出
  backgroundColor: '#f5f5f5',
  connectedBackgroundColor: '#333' // 连接背景色
});

服务端导出

对于需要大量计算或保密的场景,可以在服务端完成导出:

// 前端发送配置到后端
const exportOnServer = async () => {
  const option = chart.getOption();
  const response = await fetch('/api/export-chart', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ option })
  });
  const blob = await response.blob();
  saveAs(blob, 'server-export.png'); // 使用FileSaver.js
};

导出事件处理

可以监听导出相关事件实现自定义逻辑:

chart.on('finished', () => {
  console.log('图表渲染完成,可以安全导出');
});

// 自定义工具栏按钮触发导出
chart.setOption({
  toolbox: {
    feature: {
      myExport: {
        show: true,
        title: '自定义导出',
        icon: 'path://M...',
        onclick: () => {
          // 自定义导出逻辑
        }
      }
    }
  }
});

导出性能优化

处理大型数据集导出时的性能问题:

// 临时简化数据提高导出速度
function exportOptimized() {
  const originalOption = chart.getOption();
  
  // 简化数据系列
  const simplifiedOption = JSON.parse(JSON.stringify(originalOption));
  simplifiedOption.series.forEach(series => {
    if (series.data && series.data.length > 1000) {
      series.data = downsampleData(series.data, 500);
    }
  });
  
  // 使用简化配置导出
  chart.setOption(simplifiedOption, { silent: true });
  const imgUrl = chart.getDataURL();
  
  // 恢复原始配置
  chart.setOption(originalOption, { silent: true });
  
  return imgUrl;
}

// 降采样函数示例
function downsampleData(data, maxPoints) {
  if (data.length <= maxPoints) return data;
  const step = Math.floor(data.length / maxPoints);
  return data.filter((_, index) => index % step === 0);
}

导出样式定制

确保导出结果与屏幕显示一致:

// 强制使用打印样式
function getPrintStyleUrl() {
  const style = document.createElement('style');
  style.innerHTML = `
    @media print {
      body { background: white !important; }
      .chart-container { 
        width: 100% !important; 
        height: auto !important;
      }
    }
  `;
  
  const blob = new Blob([style.innerHTML], { type: 'text/css' });
  return URL.createObjectURL(blob);
}

// 应用打印样式
const printWindow = window.open('', '_blank');
printWindow.document.write(`
  <link rel="stylesheet" href="${getPrintStyleUrl()}" />
  <div class="chart-container">${document.getElementById('chart').outerHTML}</div>
`);

动态水印添加

导出时自动添加水印信息:

function exportWithWatermark() {
  const canvas = chart.getDom().querySelector('canvas');
  const ctx = canvas.getContext('2d');
  
  // 添加水印
  ctx.font = '20px Arial';
  ctx.fillStyle = 'rgba(0,0,0,0.1)';
  ctx.rotate(-20 * Math.PI / 180);
  
  for (let i = -5; i < 15; i++) {
    for (let j = -5; j < 15; j++) {
      ctx.fillText('CONFIDENTIAL', i * 200, j * 100);
    }
  }
  
  // 重置变换
  ctx.setTransform(1, 0, 0, 1, 0, 0);
  
  // 导出带水印的图片
  return canvas.toDataURL('image/png');
}

导出前的数据处理

在导出前对图表数据进行最后处理:

function preprocessBeforeExport() {
  const option = chart.getOption();
  
  // 1. 确保所有数据标签可见
  option.series.forEach(series => {
    if (series.label) {
      series.label.show = true;
      series.label.position = 'top';
    }
  });
  
  // 2. 调整图例位置
  if (option.legend) {
    option.legend.top = 'bottom';
  }
  
  // 3. 禁用动画确保导出时状态稳定
  option.animation = false;
  
  // 应用预处理配置
  chart.setOption(option);
  
  // 返回Promise确保渲染完成
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(chart.getDataURL());
    }, 500);
  });
}

多主题导出

支持根据不同场景导出不同主题的图表:

const themes = {
  light: {
    backgroundColor: '#ffffff',
    textStyle: { color: '#333' }
  },
  dark: {
    backgroundColor: '#1a1a1a',
    textStyle: { color: '#eee' }
  },
  print: {
    backgroundColor: '#fff',
    textStyle: { color: '#000' },
    grid: { borderWidth: 1 }
  }
};

function exportWithTheme(themeName) {
  const originalOption = chart.getOption();
  const theme = themes[themeName] || {};
  
  // 应用主题
  chart.setOption(theme, true);
  
  // 导出
  const url = chart.getDataURL();
  
  // 恢复原始主题
  chart.setOption(originalOption, true);
  
  return url;
}

导出分辨率控制

针对不同用途设置不同导出质量:

function exportWithQuality(quality) {
  let pixelRatio, type;
  
  switch(quality) {
    case 'low':
      pixelRatio = 1;
      type = 'jpeg';
      break;
    case 'medium':
      pixelRatio = 2;
      type = 'png';
      break;
    case 'high':
      pixelRatio = 3;
      type = 'png';
      break;
    case 'print':
      pixelRatio = 4;
      type = 'png';
      break;
    default:
      pixelRatio = 2;
      type = 'png';
  }
  
  return chart.getDataURL({
    type,
    pixelRatio,
    backgroundColor: '#fff'
  });
}

导出状态保存与恢复

在导出过程中保持图表状态稳定:

class ChartExporter {
  constructor(chartInstance) {
    this.chart = chartInstance;
    this.originalStates = [];
  }
  
  saveState() {
    this.originalStates.push({
      option: this.chart.getOption(),
      width: this.chart.getDom().style.width,
      height: this.chart.getDom().style.height
    });
  }
  
  restoreState() {
    const state = this.originalStates.pop();
    if (state) {
      this.chart.getDom().style.width = state.width;
      this.chart.getDom().style.height = state.height;
      this.chart.setOption(state.option);
    }
  }
  
  export(config) {
    this.saveState();
    
    // 临时调整尺寸
    if (config.width && config.height) {
      this.chart.getDom().style.width = config.width;
      this.chart.getDom().style.height = config.height;
      this.chart.resize();
    }
    
    return new Promise(resolve => {
      setTimeout(() => {
        const url = this.chart.getDataURL(config);
        this.restoreState();
        resolve(url);
      }, 300);
    });
  }
}

// 使用示例
const exporter = new ChartExporter(chart);
exporter.export({
  type: 'png',
  width: '1200px',
  height: '800px'
}).then(url => {
  console.log('Exported with custom size:', url);
});

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌