ECharts 极坐标系(polar)完全指南
文档类型: 深度技术文档
难度等级: ⭐⭐⭐
源码版本: ECharts 5.x
本文行数: 约550行
📋 目录
🎯 polar基础概念
什么是极坐标系?
极坐标系使用**角度(angle)和半径(radius)**定位数据点,而非直角坐标系的x和y。
typescript
const option = {
// 定义极坐标系
polar: {},
// 角度轴 (类似X轴)
angleAxis: {
type: 'category',
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
},
// 半径轴 (类似Y轴)
radiusAxis: {
type: 'value'
},
// 系列使用极坐标系
series: [{
type: 'bar',
coordinateSystem: 'polar', // 关键: 指定坐标系
data: [120, 200, 150, 80, 70, 110, 130]
}]
};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
应用场景:
- 雷达图 (多维对比)
- 玫瑰图 (周期性数据)
- 极坐标柱状图
📐 angleAxis与radiusAxis
angleAxis详解
typescript
const option = {
angleAxis: {
// === 类型 ===
type: 'category', // 'category' | 'value' | 'time' | 'log'
// === 分类轴配置 ===
data: ['A', 'B', 'C', 'D', 'E', 'F'],
// === 数值轴配置 ===
// type: 'value',
// min: 0,
// max: 360,
// === 起始角度 ===
startAngle: 90, // 起始角度 (默认90,从正上方开始)
// === 顺时针/逆时针 ===
clockwise: true, // 顺时针方向
// === 边界间隙 ===
boundaryGap: true, // 两侧留白
// === 分割线 ===
splitLine: {
show: true,
lineStyle: {
color: '#eee',
type: 'dashed'
}
},
// === 刻度标签 ===
axisLabel: {
show: true,
rotate: 'auto', // 自动旋转
formatter: '{value}'
}
}
};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
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
radiusAxis详解
typescript
const option = {
radiusAxis: {
// === 类型 ===
type: 'value',
// === 范围 ===
min: 0,
max: 100,
// === 对数轴 ===
// type: 'log',
// logBase: 10,
// === 分割段数 ===
splitNumber: 5,
// === 分割线 ===
splitLine: {
show: true,
lineStyle: {
color: ['#eee']
}
},
// === 轴线 ===
axisLine: {
show: true,
lineStyle: {
color: '#ccc'
}
},
// === 刻度 ===
axisTick: {
show: true,
alignWithLabel: true
}
}
};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
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
📊 极坐标图表类型
1. 极坐标柱状图 (南丁格尔玫瑰图)
typescript
const option = {
polar: {},
angleAxis: {
type: 'category',
data: ['产品A', '产品B', '产品C', '产品D', '产品E'],
boundaryGap: false,
splitLine: { show: false }
},
radiusAxis: {
type: 'value',
splitLine: {
lineStyle: { color: '#eee' }
}
},
series: [{
type: 'bar',
coordinateSystem: 'polar',
data: [80, 95, 70, 85, 90],
// 玫瑰图效果: 半径和角度都映射数据
stack: 'a',
emphasis: {
focus: 'series'
},
// 圆角
roundCap: true,
itemStyle: {
color: (params: any) => {
const colors = ['#5470C6', '#91CC75', '#FAC858', '#EE6666', '#73C0DE'];
return colors[params.dataIndex];
}
}
}]
};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
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
2. 极坐标折线图
typescript
const option = {
polar: {},
angleAxis: {
type: 'value',
startAngle: 0,
min: 0,
max: 360,
splitNumber: 12,
axisLabel: {
formatter: (value: number) => {
return value + '°';
}
}
},
radiusAxis: {
min: 0,
max: 100
},
series: [{
type: 'line',
coordinateSystem: 'polar',
data: generatePolarLineData(),
smooth: true,
// 面积填充
areaStyle: {
opacity: 0.3
},
symbol: 'circle',
symbolSize: 6
}]
};
function generatePolarLineData(): number[][] {
const data: number[][] = [];
for (let angle = 0; angle <= 360; angle += 10) {
const radius = 50 + 30 * Math.sin(angle * Math.PI / 180 * 3);
data.push([angle, radius]);
}
return data;
}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
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
3. 极坐标散点图
typescript
const option = {
polar: {},
angleAxis: {
type: 'value',
min: 0,
max: 360
},
radiusAxis: {
type: 'value',
min: 0,
max: 100
},
series: [{
type: 'scatter',
coordinateSystem: 'polar',
data: generateScatterData(100),
symbolSize: 10,
itemStyle: {
color: '#5470C6',
opacity: 0.6
}
}]
};
function generateScatterData(count: number): number[][] {
const data: number[][] = [];
for (let i = 0; i < count; i++) {
const angle = Math.random() * 360;
const radius = Math.random() * 100;
data.push([angle, radius]);
}
return data;
}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
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
4. 多层极坐标图
typescript
const option = {
polar: {},
angleAxis: {
type: 'category',
data: ['性能', '可靠性', '兼容性', '易用性', '可维护性', '安全性']
},
radiusAxis: {
min: 0,
max: 100
},
series: [
{
name: '产品A',
type: 'line',
coordinateSystem: 'polar',
data: [90, 85, 70, 80, 75, 95],
smooth: true,
areaStyle: { opacity: 0.3 }
},
{
name: '产品B',
type: 'line',
coordinateSystem: 'polar',
data: [70, 90, 85, 75, 80, 70],
smooth: true,
areaStyle: { opacity: 0.3 }
}
]
};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
💻 实战案例
案例1: 多维度能力评估
typescript
import * as echarts from 'echarts';
class CapabilityRadar {
private chart: echarts.ECharts;
constructor(container: HTMLElement) {
this.chart = echarts.init(container);
this.render();
}
private render() {
const dimensions = ['编程', '设计', '沟通', '管理', '创新', '学习'];
const option = {
title: {
text: '团队能力评估',
left: 'center'
},
legend: {
data: ['张三', '李四'],
bottom: 10
},
polar: {
radius: ['20%', '70%'] // 内径和外径
},
angleAxis: {
type: 'category',
data: dimensions,
boundaryGap: false,
splitLine: {
lineStyle: {
color: '#eee'
}
}
},
radiusAxis: {
min: 0,
max: 100,
splitNumber: 5,
axisLabel: { show: false }
},
tooltip: {
trigger: 'item'
},
series: [
{
name: '张三',
type: 'line',
coordinateSystem: 'polar',
data: [85, 70, 90, 65, 80, 95],
smooth: true,
symbol: 'circle',
symbolSize: 8,
lineStyle: { width: 2 },
areaStyle: { opacity: 0.2 }
},
{
name: '李四',
type: 'line',
coordinateSystem: 'polar',
data: [70, 90, 75, 85, 70, 80],
smooth: true,
symbol: 'circle',
symbolSize: 8,
lineStyle: { width: 2 },
areaStyle: { opacity: 0.2 }
}
]
};
this.chart.setOption(option);
}
resize() {
this.chart.resize();
}
dispose() {
this.chart.dispose();
}
}
// 使用
const radar = new CapabilityRadar(document.getElementById('chart')!);
window.addEventListener('resize', () => radar.resize());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
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
案例2: 24小时活动热力图
typescript
const option = {
polar: {
radius: ['10%', '80%']
},
angleAxis: {
type: 'value',
min: 0,
max: 24,
startAngle: 90,
clockwise: true,
axisLabel: {
formatter: (value: number) => {
return value + ':00';
}
},
splitNumber: 24
},
radiusAxis: {
type: 'category',
data: ['工作日', '周末'],
splitLine: { show: false }
},
visualMap: {
show: false,
min: 0,
max: 100,
inRange: {
color: ['#f2f2f2', '#ffeb3b', '#ff9800', '#f44336']
}
},
series: [{
type: 'heatmap',
coordinateSystem: 'polar',
data: generateActivityData(),
pointStyle: {
borderRadius: 4
}
}]
};
function generateActivityData(): number[][] {
const data: number[][] = [];
for (let hour = 0; hour < 24; hour++) {
// 工作日
let weekdayValue = 0;
if ((hour >= 9 && hour <= 12) || (hour >= 14 && hour <= 18)) {
weekdayValue = 80 + Math.random() * 20; // 工作时间高活跃度
} else if (hour >= 19 && hour <= 22) {
weekdayValue = 60 + Math.random() * 20; // 晚上中等活跃度
} else {
weekdayValue = Math.random() * 30; // 夜间低活跃度
}
data.push([hour, 0, weekdayValue]);
// 周末
let weekendValue = 0;
if (hour >= 10 && hour <= 22) {
weekendValue = 50 + Math.random() * 40; // 白天活跃
} else {
weekendValue = Math.random() * 30; // 夜间低活跃
}
data.push([hour, 1, weekendValue]);
}
return data;
}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
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
🎯 最佳实践总结
✅ DO - 推荐做法
合理设置startAngle
typescript// 从正上方开始 (符合阅读习惯) startAngle: 901
2使用boundaryGap避免边缘遮挡
typescriptangleAxis: { boundaryGap: true }1
2
3极坐标适合周期性数据
typescript// 24小时、7天、12个月等周期数据 angleAxis: { data: ['1月', '2月', ..., '12月'] }1
2
3
4
❌ DON'T - 避免做法
- 避免过多分类typescript
// ❌ 不好 - 超过12个分类会拥挤 data: Array.from({length: 30}, ...) // ✅ 好 - 控制在6-12个分类 data: ['A', 'B', 'C', 'D', 'E', 'F']1
2
3
4
5
🔗 相关资源
上一篇: cartesian2D-grid.md)
下一篇: geo地理坐标系.md)
极坐标系
{
"title": {
"text": "极坐标柱状图",
"left": "center"
},
"tooltip": {
"trigger": "axis"
},
"angleAxis": {
"type": "category",
"data": [
"周一",
"周二",
"周三",
"周四",
"周五",
"周六",
"周日"
],
"startAngle": 75
},
"radiusAxis": {
"type": "value"
},
"polar": {},
"series": [
{
"type": "bar",
"data": [
120,
200,
150,
80,
70,
110,
130
],
"coordinateSystem": "polar",
"itemStyle": {
"color": "#5470c6"
},
"emphasis": {
"itemStyle": {
"color": "#91cc75"
}
}
}
]
}上一篇: cartesian2D-grid.md)
下一篇: geo地理坐标系.md)
