echarts源码解读《五》:echarts源码之Series分析

在分析完echarts Component渲染过程之后,我们便开始进行echarts Series渲染过程的分析。

希望和小伙伴们一起进步呀!!加油!!

前言

在这篇博文中我们将探讨line(折线图)、bar(柱状图)、pie(饼图)等Series。

用户通过传递option对象来设置相应的Series。

在这部分中echarts采用了Model以及View的架构来管理Series:

  • Model:model/Series.js 管理Series数据。
  • View:view/Charts.js 负责渲染Chart视图,定义了init、render、highlight、downplay、remove等方法

Model层

model/Series.js。Series通过extend方法扩展自Component Model,重写了init等方法,并定义了getData、setData、getSource、getBaseAxis等方法。

View层

view/Charts.js。Charts中定义了init、render、highlight、downplay、remove等方法。

Line 折线图

LineSeries

LineSeries通过extend方法扩展自Series Model,重写了defaultOption属性以及getInitialData方法。

LineView

LineView通过extend方法扩展自Chart View,重写了init、render、highlight及downplay等方法,主要代码如下:

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
init: function () {
var lineGroup = new graphic.Group();
// 使用SymbolDraw绘制Symbol
var symbolDraw = new SymbolDraw();
this.group.add(symbolDraw.group);

this._symbolDraw = symbolDraw;
this._lineGroup = lineGroup;
},
render: function (seriesModel, ecModel, api) {
...
if (
!(polyline && prevCoordSys.type === coordSys.type && step === this._step)
) {
// 折线图
// symbolDraw绘制symbol
showSymbol && symbolDraw.updateData(data, {
isIgnore: isIgnoreFunc,
// createClipShape创建方法分为极坐标createPolarClipShape以及直角坐标createGridClipShape
// createPolarClipShape:通过graphic.Sector创建Clip区域
// createGridClipShape:通过graphic.Rect创建Clip区域
clipShape: createClipShape(coordSys, false, true, seriesModel)
});
...
// 通过zrender Polyline绘制折线图
polyline = this._newPolyline(points, coordSys, hasAnimation);
if (isAreaChart) {
// 通过zrender Polygon绘制折线区域
polygon = this._newPolygon(
points, stackedOnPoints,
coordSys, hasAnimation
);
}
lineGroup.setClipPath(createClipShape(coordSys, true, false, seriesModel));
}
else {
if (isAreaChart && !polygon) {
polygon = this._newPolygon(
points, stackedOnPoints,
coordSys, hasAnimation
);
}
else if (polygon && !isAreaChart) {
// If areaStyle is removed
lineGroup.remove(polygon);
polygon = this._polygon = null;
}

// Update clipPath
lineGroup.setClipPath(createClipShape(coordSys, false, false, seriesModel));

// Always update, or it is wrong in the case turning on legend
// because points are not changed
showSymbol && symbolDraw.updateData(data, {
isIgnore: isIgnoreFunc,
clipShape: createClipShape(coordSys, false, true, seriesModel)
});
...
}
...
}

Bar 柱状图

BaseBarSeries

BaseBarSeries通过extend方法扩展自Series Model,重写了defaultOption属性,以及getMarkerPosition和getInitialData方法。

BarSeries

BarSeries通过extend方法扩展自BaseBarSeries,重写了getProgressive等方法。

BarView

BarView通过extendChartView方法扩展自Chart View,重写了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
render: function (seriesModel, ecModel, api) {
// 更新绘制模式
this._updateDrawMode(seriesModel);
var coordinateSystemType = seriesModel.get('coordinateSystem');
// 支持笛卡尔坐标系以及极坐标系
if (coordinateSystemType === 'cartesian2d'
|| coordinateSystemType === 'polar'
) {
// 绘制
this._isLargeDraw
? this._renderLarge(seriesModel, ecModel, api)
: this._renderNormal(seriesModel, ecModel, api);
}
else if (__DEV__) {
console.warn('Only cartesian2d and polar supported for bar.');
}
return this.group;
}
_renderNormal: function (seriesModel, ecModel, api) {
...
data.diff(oldData)
.add(function (dataIndex) {
if (!data.hasValue(dataIndex)) {
return;
}
var itemModel = data.getItemModel(dataIndex);
// 返回layout信息
var layout = getLayout[coord.type](data, dataIndex, itemModel);
// 创建元素
// 通过graphic.Rect(笛卡尔坐标系)/ graphic.Sector(极坐标系)绘制柱形图
var el = elementCreator[coord.type](
data, dataIndex, itemModel, layout, isHorizontalOrRadial, animationModel
);
data.setItemGraphicEl(dataIndex, el);
group.add(el);
updateStyle(
el, data, dataIndex, itemModel, layout,
seriesModel, isHorizontalOrRadial, coord.type === 'polar'
);
})
.update(function (newIndex, oldIndex) {
// 更新元素
var el = oldData.getItemGraphicEl(oldIndex);
...
if (el) {
graphic.updateProps(el, {shape: layout}, animationModel, newIndex);
}
else {
el = elementCreator[coord.type](
data, newIndex, itemModel, layout, isHorizontalOrRadial, animationModel, true
);
}
...
})
.remove(function (dataIndex) {
var el = oldData.getItemGraphicEl(dataIndex);
if (coord.type === 'cartesian2d') {
el && removeRect(dataIndex, animationModel, el);
}
else {
el && removeSector(dataIndex, animationModel, el);
}
})
.execute();

this._data = data;
}

Pie 饼图

Pie.js

pie.js中注册了pieToggleSelect、pieSelect、pieUnSelect等Action

PieSeries

PieSeries通过extendSeriesModel方法扩展自Series Model,重写了defaultOption属性,以及init、mergeOption、getInitialData等方法。

PieView

PieView通过extend方法扩展自Chart View,重写了init、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
render: function (seriesModel, ecModel, api, payload) {
...
var onSectorClick = zrUtil.curry(
updateDataSelected, this.uid, seriesModel, hasAnimation, api
);

var selectedMode = seriesModel.get('selectedMode');

data.diff(oldData)
.add(function (idx) {
// 添加
// PiePiece通过graphic.Sector、graphic.Polyline及graphic.Text绘制饼图的每一块扇形
// 并绑定了emphasis、mouseover、mouseout等处理器
var piePiece = new PiePiece(data, idx);
// Default expansion animation
if (isFirstRender && animationType !== 'scale') {
piePiece.eachChild(function (child) {
child.stopAnimation(true);
});
}
// 绑定点击事件处理器
selectedMode && piePiece.on('click', onSectorClick);

data.setItemGraphicEl(idx, piePiece);

group.add(piePiece);
})
.update(function (newIdx, oldIdx) {
// 更新
var piePiece = oldData.getItemGraphicEl(oldIdx);

piePiece.updateData(data, newIdx);

piePiece.off('click');
selectedMode && piePiece.on('click', onSectorClick);
group.add(piePiece);
data.setItemGraphicEl(newIdx, piePiece);
})
.remove(function (idx) {
var piePiece = oldData.getItemGraphicEl(idx);
group.remove(piePiece);
})
.execute();
...
this._data = data;
}

PieLayout

PieLayout为饼图的布局文件,通过setItemLayout方法来设置每个Sector绘制的属性信息,包括angle、startAngle、endAngle、cx、cy等。

LabelLayout

LabelLayout用于设置饼图中每个Sector的label属性信息,包括x、y、position、height等。

Scatter 散点图

ScatterSeries

ScatterSeries通过extend方法扩展自Series Model,重写了defaultOption属性以及getInitialData等方法。

ScatterView

ScatterView通过extendChartView方法扩展自Chart View,重写了render、updateTransform等方法,主要实现代码如下:

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
render: function (seriesModel, ecModel, api) {
var data = seriesModel.getData();
// 绘制散点图symbol
// 通过SymbolDraw/LargeSymbolDraw绘制
var symbolDraw = this._updateSymbolDraw(data, seriesModel);
symbolDraw.updateData(data);
this._finished = true;
}
_updateSymbolDraw: function (data, seriesModel) {
var symbolDraw = this._symbolDraw;
var pipelineContext = seriesModel.pipelineContext;
var isLargeDraw = pipelineContext.large;

if (!symbolDraw || isLargeDraw !== this._isLargeDraw) {
symbolDraw && symbolDraw.remove();
symbolDraw = this._symbolDraw = isLargeDraw
? new LargeSymbolDraw()
: new SymbolDraw();
this._isLargeDraw = isLargeDraw;
this.group.removeAll();
}

this.group.add(symbolDraw.group);

return symbolDraw;
}

EffectScatter 带涟漪效果的散点图

EffectScatterSeries

EffectScatterSeries通过extend方法扩展自Series Model,重写了defaultOption属性以及getInitialData方法。

EffectScatterView

EffectScatterView通过extendChartView方法扩展自Chart View,重写了init、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
render: function (seriesModel, ecModel, api) {
var data = seriesModel.getData();
// 通过SymbolDraw绘制symbol
var effectSymbolDraw = this._symbolDraw;
// SymbolCtr为EffectSymbol对象实例
effectSymbolDraw.updateData(data);
this.group.add(effectSymbolDraw.group);
}
effectSymbolProto.updateData = function (data, idx) {
...
var effectCfg = {};

effectCfg.showEffectOn = seriesModel.get('showEffectOn');
effectCfg.rippleScale = itemModel.get('rippleEffect.scale');
effectCfg.brushType = itemModel.get('rippleEffect.brushType');
effectCfg.period = itemModel.get('rippleEffect.period') * 1000;
effectCfg.effectOffset = idx / data.count();
effectCfg.z = itemModel.getShallow('z') || 0;
effectCfg.zlevel = itemModel.getShallow('zlevel') || 0;
effectCfg.symbolType = symbolType;
effectCfg.color = color;

this.off('mouseover').off('mouseout').off('emphasis').off('normal');
// 设置或更新effectScatter 动画
// 通过util中的crateSymbol创建symbol以及scale缩放来实现涟漪效果
if (effectCfg.showEffectOn === 'render') {
this._effectCfg
? this.updateEffectAnimation(effectCfg)
: this.startEffectAnimation(effectCfg);

this._effectCfg = effectCfg;
}
...
this._effectCfg = effectCfg;
}

Radar 雷达图

RadarSeries

RadarSeries通过extend方法扩展自Series Model,重写了defaultOption属性以及init、getInitialData、formatTooltip方法。

RadarView

RadarView通过extendChartView方法扩展自Chart View,重写了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
render: function (seriesModel, ecModel, api) {
...
data.diff(oldData)
.add(function (idx) {
var points = data.getItemLayout(idx);
if (!points) {
return;
}
var polygon = new graphic.Polygon();
var polyline = new graphic.Polyline();
var target = {
shape: {
points: points
}
};
polygon.shape.points = getInitialPoints(points);
polyline.shape.points = getInitialPoints(points);
graphic.initProps(polygon, target, seriesModel, idx);
graphic.initProps(polyline, target, seriesModel, idx);

var itemGroup = new graphic.Group();
var symbolGroup = new graphic.Group();
itemGroup.add(polyline);
itemGroup.add(polygon);
itemGroup.add(symbolGroup);
// 更新symbol
// 通过symbolUtil.createSymbol创建symbol
updateSymbols(
polyline.shape.points, points, symbolGroup, data, idx, true
);

data.setItemGraphicEl(idx, itemGroup);
})
.update(function (newIdx, oldIdx) {
var itemGroup = oldData.getItemGraphicEl(oldIdx);
var polyline = itemGroup.childAt(0);
var polygon = itemGroup.childAt(1);
var symbolGroup = itemGroup.childAt(2);
var target = {
shape: {
points: data.getItemLayout(newIdx)
}
};
if (!target.shape.points) {
return;
}
updateSymbols(
polyline.shape.points, target.shape.points, symbolGroup, data, newIdx, false
);

graphic.updateProps(polyline, target, seriesModel);
graphic.updateProps(polygon, target, seriesModel);

data.setItemGraphicEl(newIdx, itemGroup);
})
.remove(function (idx) {
group.remove(oldData.getItemGraphicEl(idx));
})
.execute();
...

this._data = data;
}

RadarLayout

RadarLayout主要用于处理data数据,返回坐标信息。

Tree 树图

TreeAction

TreeAction注册了treeRoam及treeExpandAndCollapse等Action。

TreeSeries

TreeSeries通过extend方法扩展自Series Model,重写了defaultOption属性以及getInitialData、getOrient、setZoom、setCenter、formatTooltip等方法。

TreeLayout

TreeLayout处理data数据,返回layout属性信息。

TreeView

TreeView通过extendChartView方法扩展自Chart View,重写了init、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
render: function (seriesModel, ecModel, api, payload) {
...
// 更新坐标系以及控制器
this._updateViewCoordSys(seriesModel);
this._updateController(seriesModel, ecModel, api);

var oldData = this._data;

var seriesScope = {
expandAndCollapse: seriesModel.get('expandAndCollapse'),
layout: layout,
orient: seriesModel.getOrient(),
curvature: seriesModel.get('lineStyle.curveness'),
symbolRotate: seriesModel.get('symbolRotate'),
symbolOffset: seriesModel.get('symbolOffset'),
hoverAnimation: seriesModel.get('hoverAnimation'),
useNameLabel: true,
fadeIn: true
};

data.diff(oldData)
.add(function (newIdx) {
if (symbolNeedsDraw(data, newIdx)) {
// 创建节点以及连线
// 通过SymbolCtr来绘制symbol
// 通过graphic.BezierCurve来绘制节点间的连线
updateNode(data, newIdx, null, group, seriesModel, seriesScope);
}
})
.update(function (newIdx, oldIdx) {
var symbolEl = oldData.getItemGraphicEl(oldIdx);
if (!symbolNeedsDraw(data, newIdx)) {
symbolEl && removeNode(oldData, oldIdx, symbolEl, group, seriesModel, seriesScope);
return;
}
// 更新
updateNode(data, newIdx, symbolEl, group, seriesModel, seriesScope);
})
.remove(function (oldIdx) {
var symbolEl = oldData.getItemGraphicEl(oldIdx);
if (symbolEl) {
removeNode(oldData, oldIdx, symbolEl, group, seriesModel, seriesScope);
}
})
.execute();

this._nodeScaleRatio = seriesModel.get('nodeScaleRatio');

this._updateNodeAndLinkScale(seriesModel);

// 绑定节点click事件处理器,实现节点展开/收起效果
if (seriesScope.expandAndCollapse === true) {
data.eachItemGraphicEl(function (el, dataIndex) {
el.off('click').on('click', function () {
api.dispatchAction({
type: 'treeExpandAndCollapse',
seriesId: seriesModel.id,
dataIndex: dataIndex
});
});
});
}
this._data = data;
}

TreeMap 面积树状图

TreemapAction

TreemapAction注册了treemapZoomToNode、treemapRender、treemapMove、treemapRootToNode等Action。

TreemapSeries

TreemapSeries通过extend方法扩展自Series Model,重写了defaultOption属性以及getInitialData、formatTooltip等方法。

TreemapView

TreemapView通过extendChartView方法扩展自Chart View,重写了init、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
render: function (seriesModel, ecModel, api, payload) {
...
var renderResult = this._doRender(containerGroup, seriesModel, reRoot);
(
!isInit && (
!payloadType
|| payloadType === 'treemapZoomToNode'
|| payloadType === 'treemapRootToNode'
)
)
...
}
_doRender: function (containerGroup, seriesModel, reRoot) {
var thisTree = seriesModel.getData().tree;
var oldTree = this._oldTree;

var lastsForAnimation = createStorage();
var thisStorage = createStorage();
var oldStorage = this._storage;
var willInvisibleEls = [];
// renderNode创建Node
// 通过graphic.Rect绘制
var doRenderNode = zrUtil.curry(
renderNode, seriesModel,
thisStorage, oldStorage, reRoot,
lastsForAnimation, willInvisibleEls
);
...
return {
lastsForAnimation: lastsForAnimation,
willDeleteEls: willDeleteEls,
renderFinally: renderFinally
};
...
}

本文标题:echarts源码解读《五》:echarts源码之Series分析

文章作者:萌萌哒的邱邱邱邱

发布时间:2019年06月06日 - 14:06

最后更新:2019年07月01日 - 20:07

原始链接:https://qiuruolin.github.io/2019/06/06/echarts-5/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------本文结束感谢您的阅读-------------
感谢您的支持