2021SC@SDUSC
echarts源码分析
在ECharts中用 series表示各类图表。有line(折线图)、bar(柱状图)、pie(饼图)等Series。用户通过传递option对象来设置相应的Series。
在这部分中echarts采用了Model以及View的架构来管理Series:
Model层:model/Series.js 管理Series数据。
View:view/Charts.js 负责渲染Chart视图。
LineSeries通过extend方法扩展自Series Model,重写了defaultOption属性以及getInitialData方法。
LineView通过extend方法扩展自Chart View,重写了init、render、highlight及downplay等方法,主要代码如下:
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)
});
...
}
...
}
BaseBarSeries通过extend方法扩展自Series Model,重写了defaultOption属性,以及getMarkerPosition和getInitialData方法。
BarSeries通过extend方法扩展自BaseBarSeries,重写了getProgressive等方法。
BarView通过extendChartView方法扩展自Chart View,重写了render等方法,主要代码如下:
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.js中注册了pieToggleSelect、pieSelect、pieUnSelect等Action
PieSeries通过extendSeriesModel方法扩展自Series Model,重写了defaultOption属性,以及init、mergeOption、getInitialData等方法。
PieView通过extend方法扩展自Chart View,重写了init、render等方法,主要实现如下:
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;
}