ECharts GL 3D柱状图与散点图完全指南
文档类型: 深度技术文档
难度等级: ⭐⭐⭐⭐
源码版本: ECharts-GL 2.x
本文行数: 约650行
📋 目录
🎯 ECharts-GL安装与配置
安装方式
bash
# npm安装
npm install echarts echarts-gl
# CDN引入
<script src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts-gl/dist/echarts-gl.min.js"></script>1
2
3
4
5
6
2
3
4
5
6
基础配置
typescript
import * as echarts from 'echarts';
import 'echarts-gl';
// 验证GL是否加载成功
console.log(echarts.graphic); // 应包含3D相关方法1
2
3
4
5
2
3
4
5
📊 bar3D 3D柱状图
基础3D柱状图
typescript
const option = {
tooltip: {},
visualMap: {
max: 100,
inRange: {
color: ['#313695', '#4575b4', '#74add1', '#abd9e9', '#e0f3f8', '#ffffbf', '#fee090', '#fdae61', '#f46d43', '#d73027', '#a50026']
}
},
xAxis3D: {
type: 'category',
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
},
yAxis3D: {
type: 'category',
data: ['产品A', '产品B', '产品C', '产品D']
},
zAxis3D: {
type: 'value'
},
grid3D: {
boxWidth: 200,
boxDepth: 80,
viewControl: {
autoRotate: true // 自动旋转
},
light: {
main: {
intensity: 1.2,
shadow: true
},
ambient: {
intensity: 0.3
}
}
},
series: [{
type: 'bar3D',
data: [
[0, 0, 85], // [x索引, y索引, z值]
[0, 1, 78],
[0, 2, 92],
[0, 3, 65],
[1, 0, 88],
[1, 1, 95],
[1, 2, 82],
[1, 3, 70],
// ... 更多数据
],
shading: 'lambert', // 着色效果
label: {
show: false
}
}]
};
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
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
关键参数说明:
xAxis3D/yAxis3D/zAxis3D: 3D坐标轴grid3D: 3D网格容器shading: 着色模式 ('color','lambert','realistic')
热力图风格3D柱状图
typescript
const option = {
grid3D: {
boxWidth: 200,
boxDepth: 200,
axisLine: {
lineStyle: { color: '#fff' }
},
axisPointer: {
lineStyle: { color: '#fff' }
},
viewControl: {
distance: 300, // 视距
alpha: 30, // 视角角度
beta: 45
}
},
visualMap: {
calculable: true,
max: 100,
dimension: 2,
inRange: {
color: ['#313695', '#4575b4', '#74add1', '#abd9e9', '#e0f3f8', '#ffffbf', '#fee090', '#fdae61', '#f46d43', '#d73027', '#a50026']
}
},
series: [{
type: 'bar3D',
data: generateHeatmapData(), // 生成热力数据
shading: 'lambert',
bevelSize: 0.5, // 倒角大小
bevelSmoothness: 2,
// 光照配置
itemStyle: {
opacity: 0.9
},
emphasis: {
label: {
show: true,
formatter: (params: any) => {
return `${params.value[2]}`;
}
}
}
}]
};
function generateHeatmapData(): number[][] {
const data: number[][] = [];
for (let x = 0; x < 10; x++) {
for (let y = 0; y < 10; y++) {
const value = Math.random() * 100;
data.push([x, y, value]);
}
}
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
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
堆叠3D柱状图
typescript
const option = {
grid3D: {
boxWidth: 200,
boxDepth: 150,
viewControl: {
projection: 'orthographic' // 正交投影
}
},
series: [
{
name: 'Q1',
type: 'bar3D',
data: generateQuarterData(1),
shading: 'lambert',
stack: 'quarter' // 堆叠
},
{
name: 'Q2',
type: 'bar3D',
data: generateQuarterData(2),
shading: 'lambert',
stack: 'quarter' // 堆叠
},
{
name: 'Q3',
type: 'bar3D',
data: generateQuarterData(3),
shading: 'lambert',
stack: 'quarter' // 堆叠
}
]
};
function generateQuarterData(quarter: number): number[][] {
const data: number[][] = [];
for (let i = 0; i < 5; i++) {
for (let j = 0; j < 4; j++) {
data.push([i, j, Math.random() * 50 + 20]);
}
}
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
🔵 scatter3D 3D散点图
基础3D散点图
typescript
const option = {
tooltip: {},
grid3D: {
boxWidth: 200,
boxDepth: 200,
axisLabel: {
textStyle: { color: '#fff' }
},
axisLine: {
lineStyle: { color: '#fff' }
},
viewControl: {
distance: 250,
autoRotate: false
}
},
xAxis3D: {
type: 'value',
name: 'X轴'
},
yAxis3D: {
type: 'value',
name: 'Y轴'
},
zAxis3D: {
type: 'value',
name: 'Z轴'
},
series: [{
type: 'scatter3D',
data: generateScatterData(100),
symbolSize: 10,
itemStyle: {
color: '#5470C6',
opacity: 0.8
},
emphasis: {
itemStyle: {
color: '#EE6666'
}
}
}]
};
function generateScatterData(count: number): number[][] {
const data: number[][] = [];
for (let i = 0; i < count; i++) {
data.push([
Math.random() * 100,
Math.random() * 100,
Math.random() * 100
]);
}
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
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
带颜色映射的3D散点图
typescript
const option = {
visualMap: {
dimension: 3, // 第4维映射到颜色
min: 0,
max: 100,
calculable: true,
inRange: {
color: ['#1268A3', '#085E9E', '#085499', '#074A94', '#06408F', '#06368A', '#052C85', '#042280', '#04187B', '#030E76']
}
},
grid3D: {
boxWidth: 200,
boxDepth: 200,
environment: '#000', // 背景色
postEffect: {
enable: true,
bloom: {
enable: false
},
SSAO: {
enable: true,
radius: 2,
intensity: 1
}
}
},
series: [{
type: 'scatter3D',
data: generateColoredScatter(200),
symbolSize: 8,
// 启用后期特效
blendMode: 'lighter',
itemStyle: {
opacity: 0.6
}
}]
};
function generateColoredScatter(count: number): number[][] {
const data: number[][] = [];
for (let i = 0; i < count; i++) {
data.push([
Math.random() * 100, // x
Math.random() * 100, // y
Math.random() * 100, // z
Math.random() * 100 // 颜色维度
]);
}
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
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
动态3D散点图 (实时数据流)
typescript
class Scatter3DRealtime {
private chart: echarts.ECharts;
private data: number[][] = [];
private maxPoints = 500;
constructor(container: HTMLElement) {
this.chart = echarts.init(container);
this.initChart();
this.startDataStream();
}
private initChart() {
const option = {
grid3D: {
boxWidth: 200,
boxDepth: 200,
viewControl: {
autoRotate: true,
autoRotateSpeed: 5
}
},
xAxis3D: { type: 'value', min: 0, max: 100 },
yAxis3D: { type: 'value', min: 0, max: 100 },
zAxis3D: { type: 'value', min: 0, max: 100 },
series: [{
type: 'scatter3D',
data: this.data,
symbolSize: 6,
itemStyle: {
color: '#5470C6',
opacity: 0.7
}
}]
};
this.chart.setOption(option);
}
private startDataStream() {
setInterval(() => {
// 添加新点
this.data.push([
Math.random() * 100,
Math.random() * 100,
Math.random() * 100
]);
// 保持最大点数
if (this.data.length > this.maxPoints) {
this.data.shift();
}
// 更新图表
this.chart.setOption({
series: [{
data: this.data
}]
});
}, 100); // 每100ms更新
}
dispose() {
this.chart.dispose();
}
}
// 使用
const scatter3D = new Scatter3DRealtime(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
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
🎨 3D坐标系详解
grid3D配置详解
typescript
const grid3DConfig = {
// 尺寸配置
boxWidth: 200, // 宽度
boxHeight: 100, // 高度
boxDepth: 200, // 深度
// 位置配置
left: 'center',
top: 'middle',
right: 'auto',
bottom: 'auto',
// 视角控制
viewControl: {
projection: 'perspective', // 投影方式: 'perspective' | 'orthographic'
distance: 200, // 视距
alpha: 20, // 垂直旋转角度
beta: 40, // 水平旋转角度
center: [0, 0, 0], // 中心点
// 交互配置
rotateSensitivity: 1, // 旋转灵敏度
zoomSensitivity: 1, // 缩放灵敏度
panSensitivity: 1, // 平移灵敏度
// 自动旋转
autoRotate: false,
autoRotateSpeed: 10, // 旋转速度(秒/圈)
autoRotateAfterStill: 3 // 静止后多久开始自动旋转
// 鼠标控制
mouseController: {
rotate: true,
zoom: true,
pan: true
}
},
// 光照配置
light: {
main: {
intensity: 1,
color: '#fff',
shadow: true,
shadowQuality: 'medium', // 'low' | 'medium' | 'high' | 'ultra'
alpha: 30,
beta: 40
},
ambient: {
intensity: 0.3,
color: '#fff'
}
},
// 环境配置
environment: '#000', // 背景色或环境贴图
// 后期特效
postEffect: {
enable: false,
bloom: {
enable: false
},
SSAO: {
enable: false,
radius: 2,
intensity: 1,
quality: 'medium'
},
depthOfField: {
enable: false,
blurRadius: 3,
fstop: 2.8
}
}
};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
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
🚀 性能优化与最佳实践
1. 大数据量优化
typescript
const option = {
series: [{
type: 'scatter3D',
data: largeDataset, // 10000+ 点
// 性能优化
progressive: 1000, // 渐进式渲染
progressiveThreshold: 5000, // 超过5000点启用
// 简化渲染
shading: 'color', // 不使用复杂光照
symbolSize: 4, // 减小符号大小
// 关闭标签
label: { show: false }
}]
};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
2. 移动端适配
typescript
const isMobile = window.innerWidth < 768;
const option = {
grid3D: {
boxWidth: isMobile ? 150 : 200,
boxDepth: isMobile ? 150 : 200,
viewControl: {
autoRotate: !isMobile, // 移动端关闭自动旋转
rotateSensitivity: isMobile ? 2 : 1 // 移动端提高灵敏度
}
},
series: [{
type: 'scatter3D',
symbolSize: isMobile ? 8 : 6, // 移动端增大触点
data: scatterData
}]
};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
3. 内存管理
typescript
class Chart3DManager {
private chart: echarts.ECharts | null = null;
init(container: HTMLElement) {
this.chart = echarts.init(container);
}
update(option: any) {
this.chart?.setOption(option);
}
// 重要: 销毁时清理WebGL资源
destroy() {
if (this.chart) {
this.chart.dispose();
this.chart = null;
}
}
}
// 使用
const manager = new Chart3DManager();
manager.init(container);
// 页面卸载时
window.addEventListener('beforeunload', () => {
manager.destroy();
});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
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
📊 性能对比测试
| 数据类型 | 点数 | 帧率(FPS) | 内存(MB) |
|---|---|---|---|
| scatter3D | 1000 | 60 | 45 |
| scatter3D | 5000 | 45 | 120 |
| scatter3D | 10000 | 25 | 230 |
| bar3D | 1000 | 55 | 65 |
| bar3D | 5000 | 30 | 180 |
优化建议:
- 超过5000点: 启用progressive
- 超过10000点: 考虑数据抽样
- 移动端: 限制在3000点以内
🔗 相关资源
下一篇: surface曲面图
基础散点图
{
"title": {
"text": "身高体重关系",
"left": "center"
},
"tooltip": {
"trigger": "item"
},
"xAxis": {
"type": "value",
"name": "身高(cm)",
"min": 150,
"max": 200
},
"yAxis": {
"type": "value",
"name": "体重(kg)",
"min": 40,
"max": 100
},
"series": [
{
"type": "scatter",
"symbolSize": 15,
"data": [
[
161.2,
51.6
],
[
167.5,
59
],
[
159.5,
49.2
],
[
157,
63
],
[
155.8,
53.6
],
[
170,
59
],
[
159.1,
47.6
],
[
166,
69.8
],
[
176.2,
66.8
],
[
160.2,
75.2
],
[
172.5,
55.2
],
[
170.9,
54.2
],
[
172.9,
62.5
],
[
153.4,
42
],
[
160,
50
],
[
176.5,
71.8
]
],
"itemStyle": {
"color": "#5470c6",
"opacity": 0.8
}
}
]
}下一篇: surface曲面图
