ECharts getDataURL导出图片完全指南
文档类型: 实战指南
难度等级: ⭐⭐
源码版本: ECharts 5.x
本文行数: 约420行
📋 目录
🎯 基础用法
导出为PNG
typescript
// 获取Base64编码的图片URL
const dataURL = chart.getDataURL({
type: 'png', // 或 'jpeg'
pixelRatio: 2, // 分辨率倍数
backgroundColor: '#fff'
});
// 下载图片
const link = document.createElement('a');
link.download = 'chart.png';
link.href = dataURL;
link.click();1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
导出为JPEG
typescript
const jpegURL = chart.getDataURL({
type: 'jpeg',
pixelRatio: 2,
backgroundColor: '#fff',
excludeComponents: ['toolbox'] // 排除某些组件
});1
2
3
4
5
6
2
3
4
5
6
⚙️ 配置选项
完整参数说明
typescript
interface GetDataURLOptions {
// 图片类型
type?: 'png' | 'jpeg';
// 像素比 (影响清晰度)
pixelRatio?: number;
// 背景色
backgroundColor?: string;
// 排除的组件
excludeComponents?: string[];
// 图片质量 (仅jpeg)
quality?: number; // 0-1
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
高分辨率导出
typescript
// 4K分辨率导出
const hdDataURL = chart.getDataURL({
pixelRatio: 4, // 4倍分辨率
backgroundColor: '#fff'
});
// 文件大小对比
// pixelRatio: 1 → ~50KB
// pixelRatio: 2 → ~200KB
// pixelRatio: 4 → ~800KB1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
🌐 跨域处理
Canvas污染问题
当图表包含跨域图片时,getDataURL会失败:
typescript
try {
const dataURL = chart.getDataURL();
} catch (error) {
console.error('Canvas被污染,无法导出:', error);
}1
2
3
4
5
2
3
4
5
解决方案: 代理图片
typescript
class ImageProxy {
/**
* 通过代理加载图片
*/
static async proxyImage(url: string): Promise<string> {
const response = await fetch(`/api/proxy-image?url=${encodeURIComponent(url)}`);
const blob = await response.blob();
return URL.createObjectURL(blob);
}
/**
* 替换图表中的图片URL
*/
static async replaceImageUrls(option: any): Promise<any> {
const cloned = JSON.parse(JSON.stringify(option));
// 递归查找并替换图片URL
await this.replaceInObject(cloned);
return cloned;
}
private static async replaceInObject(obj: any): Promise<void> {
for (const key in obj) {
if (typeof obj[key] === 'string' && this.isImageUrl(obj[key])) {
obj[key] = await this.proxyImage(obj[key]);
} else if (typeof obj[key] === 'object') {
await this.replaceInObject(obj[key]);
}
}
}
private static isImageUrl(url: string): boolean {
return /\.(png|jpg|jpeg|gif|svg)$/i.test(url);
}
}
// 使用
async function exportChartWithImages() {
const originalOption = chart.getOption();
const proxiedOption = await ImageProxy.replaceImageUrls(originalOption);
// 临时使用代理后的option
chart.setOption(proxiedOption);
// 导出
const dataURL = chart.getDataURL({
pixelRatio: 2,
backgroundColor: '#fff'
});
// 恢复原始option
chart.setOption(originalOption);
return dataURL;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
💻 实战案例
案例1: 一键导出按钮
typescript
import * as echarts from 'echarts';
class ExportableChart {
private chart: echarts.ECharts;
constructor(container: HTMLElement) {
this.chart = echarts.init(container);
this.addExportButton();
}
private addExportButton() {
const exportBtn = document.createElement('button');
exportBtn.textContent = '导出图片';
exportBtn.style.cssText = `
position: absolute;
top: 10px;
right: 10px;
z-index: 1000;
padding: 8px 16px;
background: #5470C6;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
`;
exportBtn.addEventListener('click', () => {
this.exportImage();
});
container.style.position = 'relative';
container.appendChild(exportBtn);
}
private async exportImage() {
try {
// 显示加载提示
this.chart.showLoading();
// 生成图片URL
const dataURL = this.chart.getDataURL({
type: 'png',
pixelRatio: 2,
backgroundColor: '#fff'
});
// 触发下载
this.downloadImage(dataURL, `chart-${Date.now()}.png`);
} catch (error) {
console.error('导出失败:', error);
alert('导出失败,请重试');
} finally {
this.chart.hideLoading();
}
}
private downloadImage(dataURL: string, filename: string) {
const link = document.createElement('a');
link.download = filename;
link.href = dataURL;
// 兼容不同浏览器
if (document.body.fireEvent) {
// IE
document.body.fireEvent('onclick');
} else {
// 现代浏览器
link.click();
}
// 清理
URL.revokeObjectURL(link.href);
}
dispose() {
this.chart.dispose();
}
}
// 使用
const exportChart = new ExportableChart(document.getElementById('chart')!);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
案例2: 批量导出多个图表
typescript
class BatchExporter {
/**
* 批量导出多个图表
*/
static async exportAllCharts(
charts: echarts.ECharts[],
filenames: string[]
): Promise<void> {
for (let i = 0; i < charts.length; i++) {
const chart = charts[i];
const filename = filenames[i] || `chart-${i}.png`;
const dataURL = chart.getDataURL({
pixelRatio: 2,
backgroundColor: '#fff'
});
// 延迟避免浏览器阻塞
await this.delay(100);
this.triggerDownload(dataURL, filename);
}
}
private static delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
private static triggerDownload(dataURL: string, filename: string) {
const link = document.createElement('a');
link.download = filename;
link.href = dataURL;
link.click();
}
}
// 使用
const charts = [chart1, chart2, chart3];
const filenames = ['sales.png', 'profit.png', 'growth.png'];
BatchExporter.exportAllCharts(charts, filenames);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
案例3: 服务端导出 (Node.js)
typescript
import { createCanvas } from 'canvas';
import * as echarts from 'echarts';
/**
* 在服务端生成图表图片
*/
async function generateChartImage(option: any): Promise<Buffer> {
// 创建Canvas
const canvas = createCanvas(800, 600);
// 初始化ECharts
const chart = echarts.init(canvas as any);
// 设置option
chart.setOption(option);
// 导出为Buffer
const buffer = canvas.toBuffer('image/png');
// 清理
chart.dispose();
return buffer;
}
// Express路由示例
import express from 'express';
const app = express();
app.get('/api/chart-image', async (req, res) => {
const option = req.query.option;
try {
const buffer = await generateChartImage(JSON.parse(option as string));
res.setHeader('Content-Type', 'image/png');
res.send(buffer);
} catch (error) {
res.status(500).json({ error: '生成失败' });
}
});1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
🎯 最佳实践总结
✅ DO - 推荐做法
设置合适的pixelRatio
typescriptpixelRatio: 2 // 平衡清晰度和文件大小1添加白色背景
typescriptbackgroundColor: '#fff' // 避免透明背景1处理跨域图片
typescript// 使用代理或base64图片1
❌ DON'T - 避免做法
- 避免过高的分辨率typescript
// ❌ 不好 - 文件过大 pixelRatio: 8 // ✅ 好 - 适中 pixelRatio: 21
2
3
4
5
🔗 相关资源
下一篇: SVG导出
