ECharts graphic组件完全指南
文档类型: 深度技术文档
难度等级: ⭐⭐⭐
源码版本: ECharts 5.x
本文行数: 约550行
📋 目录
🎯 graphic基础
什么是graphic组件?
graphic是ECharts的原生图形组件,基于ZRender提供底层图形能力,用于绘制装饰性元素、标注、水印等。
typescript
const option = {
graphic: {
elements: [
{
type: 'text',
left: 'center',
top: 'middle',
style: {
text: 'Hello ECharts',
fill: '#333',
fontSize: 24
}
}
]
}
};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
与custom系列的区别:
graphic: 全局装饰元素,不绑定数据custom: 数据驱动的自定义图表
🎨 图形元素类型
1. 文本(text)
typescript
const textElement = {
type: 'text',
left: 100,
top: 50,
rotation: 0, // 旋转角度
scale: [1, 1], // 缩放
originAt: ['center', 'center'], // 变换原点
style: {
text: '标题文本',
x: 0, // 文本内部偏移
y: 0,
textAlign: 'center',
textVerticalAlign: 'middle',
fill: '#333', // 填充色
stroke: '#fff', // 描边色
lineWidth: 2,
fontSize: 18,
fontFamily: 'Arial',
fontWeight: 'bold',
// 富文本
rich: {
a: { fill: 'red', fontSize: 20 },
b: { fill: 'blue', fontSize: 16 }
}
},
emphasis: {
style: {
fill: '#ff0000'
}
}
};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
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
2. 矩形(rect)
typescript
const rectElement = {
type: 'rect',
left: 100,
top: 100,
shape: {
x: 0,
y: 0,
width: 200,
height: 100,
r: 10 // 圆角半径
},
style: {
fill: '#5470C6',
stroke: '#333',
lineWidth: 2,
shadowBlur: 10,
shadowColor: 'rgba(0,0,0,0.3)'
}
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
3. 圆形(circle)
typescript
const circleElement = {
type: 'circle',
left: 'center',
top: 'center',
shape: {
cx: 0,
cy: 0,
r: 50
},
style: {
fill: '#91CC75',
stroke: '#fff',
lineWidth: 3
}
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
4. 图片(image)
typescript
const imageElement = {
type: 'image',
left: 50,
top: 50,
style: {
image: '/logo.png', // 图片URL或Canvas
x: 0,
y: 0,
width: 100,
height: 100,
opacity: 0.8
}
};1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
5. 分组(group)
typescript
const groupElement = {
type: 'group',
left: 100,
top: 100,
children: [
{
type: 'rect',
shape: { x: 0, y: 0, width: 100, height: 50 },
style: { fill: '#5470C6' }
},
{
type: 'text',
style: {
text: '标签',
x: 50,
y: 25,
textAlign: 'center',
textVerticalAlign: 'middle',
fill: '#fff'
}
}
]
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
6. 路径(path) - SVG路径
typescript
const pathElement = {
type: 'path',
left: 100,
top: 100,
shape: {
d: 'M 0 0 L 100 0 L 100 100 L 0 100 Z', // SVG路径
x: 0,
y: 0,
width: 100,
height: 100
},
style: {
fill: '#FAC858',
stroke: '#333',
lineWidth: 2
}
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
7. 线条(line)
typescript
const lineElement = {
type: 'line',
shape: {
x1: 0,
y1: 0,
x2: 200,
y2: 100
},
style: {
stroke: '#EE6666',
lineWidth: 2,
lineDash: [5, 5] // 虚线
}
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
8. 多边形(polygon)
typescript
const polygonElement = {
type: 'polygon',
shape: {
points: [
[0, 0],
[100, 0],
[150, 80],
[50, 80]
]
},
style: {
fill: '#73C0DE',
stroke: '#333',
lineWidth: 2
}
};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
const animatedElement = {
type: 'rect',
left: 100,
top: 100,
shape: {
x: 0,
y: 0,
width: 100,
height: 50
},
style: { fill: '#5470C6' },
// 入场动画
transition: ['shape', 'style'],
duration: 1000,
easing: 'cubicInOut',
// 延迟
delay: 500
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
交互式高亮
typescript
const interactiveElement = {
type: 'circle',
left: 'center',
top: 'center',
shape: { cx: 0, cy: 0, r: 50 },
style: { fill: '#5470C6' },
// 鼠标悬停样式
emphasis: {
style: {
fill: '#EE6666',
scale: [1.2, 1.2]
}
},
// 鼠标按下样式
downplay: {
style: {
opacity: 0.5
}
},
// 点击事件
onclick: () => {
console.log('Clicked!');
}
};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
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
💻 实战案例
案例1: 水印
typescript
const watermarkOption = {
graphic: {
elements: Array.from({ length: 20 }, (_, i) => ({
type: 'text',
rotation: Math.PI / 4,
left: (i % 5) * 25 + '%',
top: Math.floor(i / 5) * 25 + '%',
z: 0, // 置于底层
style: {
text: 'CONFIDENTIAL',
x: 0,
y: 0,
textAlign: 'center',
textVerticalAlign: 'middle',
fill: 'rgba(0, 0, 0, 0.05)',
fontSize: 20,
fontWeight: 'bold'
},
silent: true // 不响应事件
}))
}
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
案例2: 自定义标注
typescript
const annotationOption = {
graphic: {
elements: [
// 箭头标注
{
type: 'path',
left: '60%',
top: '40%',
shape: {
d: 'M 0 0 L 20 -10 L 20 10 Z',
x: 0,
y: 0,
width: 20,
height: 20
},
style: { fill: '#EE6666' },
rotation: Math.PI / 2
},
// 标注文字
{
type: 'rect',
left: '62%',
top: '35%',
shape: { x: 0, y: 0, width: 100, height: 30, r: 5 },
style: {
fill: 'rgba(238, 102, 102, 0.9)',
stroke: '#EE6666',
lineWidth: 2
}
},
{
type: 'text',
left: '62%',
top: '35%',
style: {
text: '异常峰值',
x: 50,
y: 15,
textAlign: 'center',
textVerticalAlign: 'middle',
fill: '#fff',
fontSize: 12
}
}
]
}
};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
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
案例3: Logo和版权信息
typescript
const brandingOption = {
graphic: {
elements: [
// Logo
{
type: 'image',
right: 20,
bottom: 20,
style: {
image: '/company-logo.png',
x: 0,
y: 0,
width: 80,
height: 40
},
z: 100
},
// 版权文字
{
type: 'text',
right: 110,
bottom: 30,
style: {
text: '© 2024 Company',
x: 0,
y: 0,
textAlign: 'right',
fill: '#999',
fontSize: 10
},
z: 100
}
]
}
};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
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
案例4: 动态进度环
typescript
class ProgressRing {
private chart: echarts.ECharts;
private progress: number = 0;
constructor(container: HTMLElement) {
this.chart = echarts.init(container);
this.render();
this.animate();
}
private render() {
const option = {
graphic: {
elements: [
// 背景圆环
{
type: 'arc',
left: 'center',
top: 'center',
shape: {
cx: 0,
cy: 0,
r: 80,
startAngle: 0,
endAngle: Math.PI * 2
},
style: {
stroke: '#e0e0e0',
lineWidth: 10
}
},
// 进度圆环
{
type: 'arc',
left: 'center',
top: 'center',
shape: {
cx: 0,
cy: 0,
r: 80,
startAngle: -Math.PI / 2,
endAngle: -Math.PI / 2
},
style: {
stroke: '#5470C6',
lineWidth: 10,
lineCap: 'round'
},
keyframeAnimation: {
duration: 2000,
loop: false,
keyframes: [
{
percent: 1,
shape: {
endAngle: -Math.PI / 2 + Math.PI * 2 * (this.progress / 100)
}
}
]
}
},
// 百分比文字
{
type: 'text',
left: 'center',
top: 'center',
style: {
text: `${this.progress}%`,
x: 0,
y: 0,
textAlign: 'center',
textVerticalAlign: 'middle',
fill: '#333',
fontSize: 24,
fontWeight: 'bold'
}
}
]
}
};
this.chart.setOption(option);
}
private animate() {
let current = 0;
const timer = setInterval(() => {
current += 1;
if (current > 100) {
clearInterval(timer);
return;
}
this.progress = current;
this.render();
}, 20);
}
dispose() {
this.chart.dispose();
}
}
// 使用
const progress = new ProgressRing(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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
🎯 最佳实践总结
✅ DO - 推荐做法
使用z-index控制层级
typescriptz: 100 // 确保在最上层1装饰元素设置silent
typescriptsilent: true // 不干扰图表交互1使用相对定位
typescriptleft: 'center', top: '50%'1
2
❌ DON'T - 避免做法
- 避免过多graphic元素typescript
// ❌ 不好 - 超过50个元素 elements: Array.from({length: 100}, ...) // ✅ 好 - 控制在10个以内 elements: essentialElements1
2
3
4
5
🔗 相关资源
下一篇: 插件开发指南
关系图效果
{
"title": {
"text": "社交网络关系",
"left": "center"
},
"tooltip": {},
"animationDurationUpdate": 1500,
"animationEasingUpdate": "quinticInOut",
"series": [
{
"type": "graph",
"layout": "force",
"symbolSize": 50,
"roam": true,
"label": {
"show": true
},
"edgeSymbol": [
"circle",
"arrow"
],
"edgeSymbolSize": [
4,
10
],
"force": {
"repulsion": 100,
"gravity": 0.1,
"edgeLength": 150
},
"data": [
{
"name": "Alice",
"x": 100,
"y": 100
},
{
"name": "Bob",
"x": 300,
"y": 100
},
{
"name": "Charlie",
"x": 200,
"y": 250
},
{
"name": "David",
"x": 400,
"y": 250
},
{
"name": "Eve",
"x": 250,
"y": 400
}
],
"links": [
{
"source": "Alice",
"target": "Bob"
},
{
"source": "Alice",
"target": "Charlie"
},
{
"source": "Bob",
"target": "David"
},
{
"source": "Charlie",
"target": "Eve"
},
{
"source": "David",
"target": "Eve"
}
],
"lineStyle": {
"color": "source",
"curveness": 0.3
}
}
]
}下一篇: 插件开发指南
