2021SC@SDUSC
Component源码解读---接上篇
plain
legend.plain为平面的legend图例组件,主要包含LegendAction、LegendModel和LegendView文件。
import LegendModel from './LegendModel';
import LegendView from './LegendView';
import legendFilter from './legendFilter';
import { installLegendAction } from './legendAction';
export function install(registers) {
registers.registerComponentModel(LegendModel);
registers.registerComponentView(LegendView);
registers.registerProcessor(registers.PRIORITY.PROCESSOR.SERIES_FILTER, legendFilter);
registers.registerSubTypeDefaulter('legend', function () {
return 'plain';
});
installLegendAction(registers);
}
LegendAction文件中注册了legend的对外API: legendToggleSelect、legendAllSelect、legendInverseSelect、legendSelect和legendUnSelect。
registers.registerAction('legendToggleSelect', 'legendselectchanged', curry(legendSelectActionHandler, 'toggleSelected'));
registers.registerAction('legendAllSelect', 'legendselectall', curry(legendSelectActionHandler, 'allSelect'));
registers.registerAction('legendInverseSelect', 'legendinverseselect', curry(legendSelectActionHandler, 'inverseSelect'));
registers.registerAction('legendSelect', 'legendselected', curry(legendSelectActionHandler, 'select'));
registers.registerAction('legendUnSelect', 'legendunselected', curry(legendSelectActionHandler, 'unSelect'));
LegendFilter文件中当legend的isSelected返回true,则显示与legend name相同series的数据,即实现数据过滤。
export default function legendFilter(ecModel) {
var legendModels = ecModel.findComponents({
mainType: 'legend'
});
if (legendModels && legendModels.length) {
ecModel.filterSeries(function (series) {
// If in any legend component the status is not selected.
// Because in legend series is assumed selected when it is not in the legend data.
for (var i = 0; i < legendModels.length; i++) {
if (!legendModels[i].isSelected(series.name)) { //isSelected返回false,则与legend name相同的series数据不显示
return false;
}
}
return true;
});
}
}
LegendModel通过extendComponentModel方法扩展自Component Model,重写defaultOption属性,重写了init方法,定义了select、unSelect、toggleSelected以及isSelected等方法。
LegendView 通过extendComponentView方法扩展自Component View,重写了init以及render方法对legend进行渲染.
//重写init
LegendView.prototype.init = function () {
this.group.add(this._contentGroup = new Group());
this.group.add(this._selectorGroup = new Group());
this._isFirstRender = true;
};
//重写render
LegendView.prototype.render = function (legendModel, ecModel, api) {
var isFirstRender = this._isFirstRender;
this._isFirstRender = false;
this.resetInner();
if (!legendModel.get('show', true)) {
return;
}
var itemAlign = legendModel.get('align');
var orient = legendModel.get('orient');
if (!itemAlign || itemAlign === 'auto') {
itemAlign = legendModel.get('left') === 'right' && orient === 'vertical' ? 'right' : 'left';
} // selector has been normalized to an array in model
var selector = legendModel.get('selector', true);
var selectorPosition = legendModel.get('selectorPosition', true);
if (selector && (!selectorPosition || selectorPosition === 'auto')) {
selectorPosition = orient === 'horizontal' ? 'end' : 'start';
}
this.renderInner(itemAlign, legendModel, ecModel, api, selector, orient, selectorPosition); // Perform layout.
//见下面代码LegendView.prototype.renderInner
var positionInfo = legendModel.getBoxLayoutParams();
var viewportSize = {
width: api.getWidth(),
height: api.getHeight()
};
var padding = legendModel.get('padding');
var maxSize = layoutUtil.getLayoutRect(positionInfo, viewportSize, padding);
var mainRect = this.layoutInner(legendModel, itemAlign, maxSize, isFirstRender, selector, selectorPosition); // Place mainGroup, based on the calculated `mainRect`.
var layoutRect = layoutUtil.getLayoutRect(zrUtil.defaults({
width: mainRect.width,
height: mainRect.height
}, positionInfo), viewportSize, padding);
this.group.x = layoutRect.x - mainRect.x;
this.group.y = layoutRect.y - mainRect.y;
this.group.markRedraw(); // Render background after group is layout.
this.group.add(this._backgroundEl = makeBackground(mainRect, legendModel));
};
// renderInner中调用了createItem方法创建Legend item
// 并绑定了click、mouseover、mouseout等事件
LegendView.prototype.renderInner = function (itemAlign, legendModel, ecModel, api, selector, orient, selectorPosition) {
var contentGroup = this.getContentGroup();
var legendDrawnMap = zrUtil.createHashMap();
var selectMode = legendModel.get('selectedMode');
var excludeSeriesId = [];
ecModel.eachRawSeries(function (seriesModel) {
!seriesModel.get('legendHoverLink') && excludeSeriesId.push(seriesModel.id);
});
each(legendModel.getData(), function (legendItemModel, dataIndex) {
var name = legendItemModel.get('name'); // Use empty string or \n as a newline string
if (!this.newlineDisabled && (name === '' || name === '\n')) {
var g = new Group(); // @ts-ignore
g.newline = true;
contentGroup.add(g);
return;
} // Representitive series.
var seriesModel = ecModel.getSeriesByName(name)[0];
if (legendDrawnMap.get(name)) {
// Have been drawed
return;
} // Legend to control series.
if (seriesModel) {
var data = seriesModel.getData();
var lineVisualStyle = data.getVisual('legendLineStyle') || {};
var legendIcon = data.getVisual('legendIcon');
/**
* `data.getVisual('style')` may be the color from the register
* in series. For example, for line series,
*/
var style = data.getVisual('style');
var itemGroup = this._createItem(seriesModel, name, dataIndex, legendItemModel, legendModel, itemAlign, lineVisualStyle, style, legendIcon, selectMode);//创建Item
itemGroup.on('click', curry(dispatchSelectAction, name, null, api, excludeSeriesId)).on('mouseover', curry(dispatchHighlightAction, seriesModel.name, null, api, excludeSeriesId)).on('mouseout', curry(dispatchDownplayAction, seriesModel.name, null, api, excludeSeriesId));//绑定了click、mouseover、mouseout等事件
legendDrawnMap.set(name, true);
} else {
// Legend to control data. In pie and funnel.
ecModel.eachRawSeries(function (seriesModel) {
// In case multiple series has same data name
if (legendDrawnMap.get(name)) {
return;
}
if (seriesModel.legendVisualProvider) {
var provider = seriesModel.legendVisualProvider;
if (!provider.containName(name)) {
return;
}
var idx = provider.indexOfName(name);
var style = provider.getItemVisual(idx, 'style');
var legendIcon = provider.getItemVisual(idx, 'legendIcon');
var colorArr = parse(style.fill); // Color may be set to transparent in visualMap when data is out of range.
// Do not show nothing.
if (colorArr && colorArr[3] === 0) {
colorArr[3] = 0.2; // TODO color is set to 0, 0, 0, 0. Should show correct RGBA
style.fill = stringify(colorArr, 'rgba');
}
var itemGroup = this._createItem(seriesModel, name, dataIndex, legendItemModel, legendModel, itemAlign, {}, style, legendIcon, selectMode); // FIXME: consider different series has items with the same name.
itemGroup.on('click', curry(dispatchSelectAction, null, name, api, excludeSeriesId)) // Should not specify the series name, consider legend controls
// more than one pie series.
.on('mouseover', curry(dispatchHighlightAction, null, name, api, excludeSeriesId)).on('mouseout', curry(dispatchDownplayAction, null, name, api, excludeSeriesId));
legendDrawnMap.set(name, true);
}
}, this);
}
if (process.env.NODE_ENV !== 'production') {
if (!legendDrawnMap.get(name)) {
console.warn(name + ' series not exists. Legend data should be same with series name or data name.');
}
}
}, this);
if (selector) {
this._createSelector(selector, legendModel, api, orient, selectorPosition);
}
};
//创建Item
LegendView.prototype._createItem = function (seriesModel, name, dataIndex, legendItemModel, legendModel, itemAlign, lineVisualStyle, itemVisualStyle, legendIcon, selectMode) {
var drawType = seriesModel.visualDrawType;
var itemWidth = legendModel.get('itemWidth');
var itemHeight = legendModel.get('itemHeight');
var isSelected = legendModel.isSelected(name);
var iconRotate = legendItemModel.get('symbolRotate');
var symbolKeepAspect = legendItemModel.get('symbolKeepAspect');
var legendIconType = legendItemModel.get('icon');
legendIcon = legendIconType || legendIcon || 'roundRect';
var style = getLegendStyle(legendIcon, legendItemModel, lineVisualStyle, itemVisualStyle, drawType, isSelected);
var itemGroup = new Group();
var textStyleModel = legendItemModel.getModel('textStyle');
if (typeof seriesModel.getLegendIcon === 'function' && (!legendIconType || legendIconType === 'inherit')) {
// Series has specific way to define legend icon
itemGroup.add(seriesModel.getLegendIcon({
itemWidth: itemWidth,
itemHeight: itemHeight,
icon: legendIcon,
iconRotate: iconRotate,
itemStyle: style.itemStyle,
lineStyle: style.lineStyle,
symbolKeepAspect: symbolKeepAspect
}));
} else {
// Use default legend icon policy for most series
var rotate = legendIconType === 'inherit' && seriesModel.getData().getVisual('symbol') ? iconRotate === 'inherit' ? seriesModel.getData().getVisual('symbolRotate') : iconRotate : 0; // No rotation for no icon
itemGroup.add(getDefaultLegendIcon({
itemWidth: itemWidth,
itemHeight: itemHeight,
icon: legendIcon,
iconRotate: rotate,
itemStyle: style.itemStyle,
lineStyle: style.lineStyle,
symbolKeepAspect: symbolKeepAspect
}));
}
var textX = itemAlign === 'left' ? itemWidth + 5 : -5;
var textAlign = itemAlign;
var formatter = legendModel.get('formatter');
var content = name;
if (typeof formatter === 'string' && formatter) {
content = formatter.replace('{name}', name != null ? name : '');
} else if (typeof formatter === 'function') {
content = formatter(name);
}
var inactiveColor = legendItemModel.get('inactiveColor');
itemGroup.add(new graphic.Text({
style: createTextStyle(textStyleModel, {
text: content,
x: textX,
y: itemHeight / 2,
fill: isSelected ? textStyleModel.getTextColor() : inactiveColor,
align: textAlign,
verticalAlign: 'middle'
})
})); // Add a invisible rect to increase the area of mouse hover
var hitRect = new graphic.Rect({
shape: itemGroup.getBoundingRect(),
invisible: true
});
var tooltipModel = legendItemModel.getModel('tooltip');
if (tooltipModel.get('show')) {
graphic.setTooltipConfig({
el: hitRect,
componentModel: legendModel,
itemName: name,
itemTooltipOption: tooltipModel.option
});
}
itemGroup.add(hitRect);
itemGroup.eachChild(function (child) {
child.silent = true;
});
hitRect.silent = !selectMode;
this.getContentGroup().add(itemGroup);
enableHoverEmphasis(itemGroup); // @ts-ignore
itemGroup.__legendDataIndex = dataIndex;
return itemGroup;
};