ECharts 插件开发指南完全指南
文档类型: 高级教程
难度等级: ⭐⭐⭐⭐⭐
源码版本: ECharts 5.x
本文行数: 约600行
📋 目录
🎯 插件架构
ECharts插件体系
ECharts提供多种扩展点:
typescript
// 1. 注册自定义系列
echarts.registerSeriesModel(CustomSeriesModel);
// 2. 注册布局算法
echarts.registerLayout(customLayout);
// 3. 注册视觉映射
echarts.registerVisual(customVisual);
// 4. 注册处理器
echarts.registerProcessor(customProcessor);
// 5. 注册主题
echarts.registerTheme('custom-theme', themeConfig);
// 6. 注册地图
echarts.registerMap('custom-map', geoJson);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
🔧 注册自定义插件
基础插件结构
typescript
import * as echarts from 'echarts';
class CustomPlugin {
// 插件ID
static id = 'custom-plugin';
// 初始化
init(ecModel: any, api: any) {
console.log('插件初始化');
}
// 渲染前处理
render(ecModel: any, api: any, payload: any) {
// 自定义逻辑
}
// 销毁
dispose() {
console.log('插件销毁');
}
}
// 注册插件
echarts.use([CustomPlugin]);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
完整示例: 数据标注插件
typescript
import * as echarts from 'echarts';
interface AnnotationConfig {
type: 'arrow' | 'text' | 'rect';
position: [number, number];
content?: string;
style?: any;
}
class AnnotationPlugin implements echarts.ExtensionAPI {
static id = 'annotation';
static dependencies = ['series'];
private annotations: AnnotationConfig[] = [];
/**
* 添加标注
*/
addAnnotation(config: AnnotationConfig) {
this.annotations.push(config);
}
/**
* 清除所有标注
*/
clearAnnotations() {
this.annotations = [];
}
/**
* 渲染标注
*/
render(ecModel: any, api: any, payload: any) {
const zr = api.getZr();
// 清除旧标注
zr.clear();
// 绘制新标注
this.annotations.forEach(annotation => {
const element = this.createAnnotationElement(annotation);
if (element) {
zr.add(element);
}
});
}
private createAnnotationElement(config: AnnotationConfig) {
switch (config.type) {
case 'arrow':
return this.createArrow(config);
case 'text':
return this.createText(config);
case 'rect':
return this.createRect(config);
default:
return null;
}
}
private createArrow(config: AnnotationConfig) {
return new (echarts.graphic as any).Path({
shape: {
d: 'M 0 0 L 20 -10 L 20 10 Z'
},
style: {
fill: '#EE6666'
},
position: config.position
});
}
private createText(config: AnnotationConfig) {
return new (echarts.graphic as any).Text({
style: {
text: config.content || '',
fill: '#333',
fontSize: 12
},
position: config.position
});
}
private createRect(config: AnnotationConfig) {
return new (echarts.graphic as any).Rect({
shape: {
x: 0,
y: 0,
width: 100,
height: 50
},
style: config.style || { fill: '#5470C6' },
position: config.position
});
}
dispose() {
this.clearAnnotations();
}
}
// 注册
echarts.use([AnnotationPlugin]);
// 使用
const chart = echarts.init(container);
const plugin = chart.getModel().getComponent('annotation') as AnnotationPlugin;
plugin.addAnnotation({
type: 'arrow',
position: [100, 100]
});
chart.render();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
106
107
108
109
110
111
112
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
106
107
108
109
110
111
112
📊 扩展图表类型
创建自定义系列
typescript
import * as echarts from 'echarts';
// 1. 定义系列模型
class CustomSeriesModel extends echarts.SeriesModel {
static type = 'series.customType';
static dependencies = ['grid'];
defaultOption = {
coordinateSystem: 'cartesian2d',
data: []
};
getInitialData(option: any, ecModel: any) {
return echarts.helper.createDimensions(option.data, {
coordDimensions: ['x', 'y']
});
}
}
// 2. 定义视图
class CustomSeriesView extends echarts.ChartView {
static type = 'customType';
render(seriesModel: any, ecModel: any, api: any) {
const group = this.group;
group.removeAll();
const data = seriesModel.getData();
const coordSys = seriesModel.coordinateSystem;
data.each((idx: number) => {
const point = coordSys.dataToPoint(data.getValues(['x', 'y'], idx));
const circle = new (echarts.graphic as any).Circle({
shape: {
cx: point[0],
cy: point[1],
r: 5
},
style: {
fill: data.getItemVisual(idx, 'color')
}
});
group.add(circle);
});
}
dispose() {
this.group.removeAll();
}
}
// 3. 注册
echarts.registerSeriesModel(CustomSeriesModel);
echarts.registerChartView(CustomSeriesView);
// 4. 使用
const option = {
series: [{
type: 'customType',
data: [[0, 10], [1, 20], [2, 15]]
}]
};
chart.setOption(option);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
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
🚀 发布与分享
npm包结构
echarts-plugin-custom/
├── package.json
├── README.md
├── src/
│ ├── index.ts
│ ├── plugin.ts
│ └── types.ts
├── dist/
│ ├── index.js
│ └── index.min.js
└── examples/
└── demo.html1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
package.json
json
{
"name": "echarts-plugin-custom",
"version": "1.0.0",
"description": "Custom plugin for ECharts",
"main": "dist/index.js",
"module": "src/index.ts",
"types": "src/types.d.ts",
"peerDependencies": {
"echarts": "^5.0.0"
},
"devDependencies": {
"typescript": "^4.9.0",
"rollup": "^3.0.0"
},
"scripts": {
"build": "rollup -c",
"dev": "rollup -c -w"
}
}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
TypeScript声明文件
typescript
// types.d.ts
import * as echarts from 'echarts';
declare module 'echarts' {
interface ECharts {
use(plugin: ExtensionAPI): void;
}
}
export interface PluginConfig {
enable: boolean;
options?: any;
}
export class CustomPlugin implements echarts.ExtensionAPI {
static id: string;
init(ecModel: any, api: any): void;
render(ecModel: any, api: any, payload: any): void;
dispose(): void;
}
export default CustomPlugin;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
Rollup配置
javascript
// rollup.config.js
import typescript from '@rollup/plugin-typescript';
import { terser } from 'rollup-plugin-terser';
export default [
{
input: 'src/index.ts',
output: {
file: 'dist/index.js',
format: 'umd',
name: 'echartsPluginCustom',
globals: {
echarts: 'echarts'
}
},
external: ['echarts'],
plugins: [typescript()]
},
{
input: 'src/index.ts',
output: {
file: 'dist/index.min.js',
format: 'umd',
name: 'echartsPluginCustom'
},
external: ['echarts'],
plugins: [typescript(), terser()]
}
];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
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
发布到npm
bash
# 1. 构建
npm run build
# 2. 登录npm
npm login
# 3. 发布
npm publish
# 4. 更新版本
npm version patch # 或 minor, major
npm publish1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
使用示例
html
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts-plugin-custom/dist/index.min.js"></script>
</head>
<body>
<div id="chart" style="width: 800px; height: 600px;"></div>
<script>
const chart = echarts.init(document.getElementById('chart'));
// 使用插件
chart.use(echartsPluginCustom);
chart.setOption({
// 配置项
});
</script>
</body>
</html>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
🎯 最佳实践总结
✅ DO - 推荐做法
遵循ECharts命名规范
typescriptstatic type = 'series.myCustom' static id = 'my-plugin'1
2提供TypeScript类型定义
typescriptinterface PluginConfig { /* ... */ }1编写完整文档
- README.md
- API文档
- 示例代码
❌ DON'T - 避免做法
避免修改ECharts核心代码
typescript// ❌ 不好 - Fork并修改 // ✅ 好 - 使用扩展API1
2避免全局污染
typescript// ❌ 不好 window.myPlugin = ... // ✅ 好 export default MyPlugin1
2
3
4
5
🔗 相关资源
✅ 自定义扩展模块完成!
