1020 lines
31 KiB
Java
1020 lines
31 KiB
Java
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
||
|
||
var Util = require('../../util');
|
||
|
||
var _require = require('@antv/component/lib'),
|
||
Legend = _require.Legend;
|
||
|
||
var Tail = require('../../component/legend/tail');
|
||
|
||
var Shape = require('../../geom/shape/shape');
|
||
|
||
var bboxOfBackPlot = require('../util/bbox-of-back-plot');
|
||
|
||
var plotRange2BBox = require('../util/plot-range2bbox');
|
||
|
||
var Global = require('../../global');
|
||
|
||
var FIELD_ORIGIN = '_origin';
|
||
var MARKER_SIZE = 4.5;
|
||
var requireAnimationFrameFn = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
|
||
var STROKE_MARKERS = ['cross', 'tick', 'plus', 'hyphen', 'line', 'hollowCircle', 'hollowSquare', 'hollowDiamond', 'hollowTriangle', 'hollowTriangleDown', 'hollowHexagon', 'hollowBowtie'];
|
||
|
||
function _snapEqual(v1, v2, scale) {
|
||
var isEqual;
|
||
|
||
if (Util.isNil(scale)) {
|
||
return false;
|
||
}
|
||
|
||
v1 = scale.translate(v1);
|
||
v2 = scale.translate(v2);
|
||
|
||
if (scale.isCategory) {
|
||
isEqual = v1 === v2;
|
||
} else {
|
||
isEqual = Math.abs(v1 - v2) <= 1;
|
||
}
|
||
|
||
return isEqual;
|
||
}
|
||
|
||
function findGeom(geoms, value) {
|
||
var rst;
|
||
Util.each(geoms, function (geom) {
|
||
if (geom.get('visible')) {
|
||
var yScale = geom.getYScale();
|
||
|
||
if (yScale.field === value) {
|
||
rst = geom;
|
||
return;
|
||
}
|
||
}
|
||
});
|
||
return rst;
|
||
}
|
||
|
||
var LegendController = /*#__PURE__*/function () {
|
||
function LegendController(cfg) {
|
||
var self = this;
|
||
self.options = {};
|
||
Util.mix(self, cfg);
|
||
self.clear();
|
||
var chart = self.chart;
|
||
self.container = chart.get('frontPlot');
|
||
self.plotRange = chart.get('plotRange');
|
||
}
|
||
|
||
var _proto = LegendController.prototype;
|
||
|
||
_proto.clear = function clear() {
|
||
var legends = this.legends;
|
||
this.backRange = null;
|
||
Util.each(legends, function (legendItems) {
|
||
Util.each(legendItems, function (legend) {
|
||
legend.destroy();
|
||
});
|
||
});
|
||
this.legends = {};
|
||
} // 获取坐标轴等背景元素占的范围,防止遮挡坐标轴
|
||
;
|
||
|
||
_proto.getBackRange = function getBackRange() {
|
||
var backRange = this.backRange;
|
||
|
||
if (!backRange) {
|
||
var backPlot = this.chart.get('backPlot');
|
||
backRange = bboxOfBackPlot(backPlot, plotRange2BBox(this.chart.get('plotRange')));
|
||
var plotRange = this.plotRange;
|
||
|
||
if (backRange.maxX - backRange.minX < plotRange.br.x - plotRange.tl.x && backRange.maxY - backRange.minY < plotRange.br.y - plotRange.tl.y) {
|
||
// 如果背景小于则直接使用 plotRange
|
||
backRange = {
|
||
minX: plotRange.tl.x,
|
||
minY: plotRange.tl.y,
|
||
maxX: plotRange.br.x,
|
||
maxY: plotRange.br.y
|
||
};
|
||
}
|
||
|
||
this.backRange = backRange;
|
||
}
|
||
|
||
return backRange;
|
||
};
|
||
|
||
_proto._isFieldInView = function _isFieldInView(field, value, view) {
|
||
var flag = false;
|
||
var scales = view.get('scales');
|
||
var fieldScale = scales[field];
|
||
|
||
if (fieldScale && fieldScale.values) {
|
||
flag = Util.inArray(fieldScale.values, value);
|
||
}
|
||
|
||
return flag;
|
||
};
|
||
|
||
_proto._bindClickEvent = function _bindClickEvent(legend, scale, filterVals) {
|
||
var self = this;
|
||
var chart = self.chart;
|
||
var views = chart.get('views');
|
||
var field = scale.field;
|
||
var options = self.options;
|
||
legend.on('itemclick', function (ev) {
|
||
if (options.onClick && options.defaultClickHandlerEnabled !== true) {
|
||
options.onClick(ev);
|
||
} else {
|
||
// if 'defaultClickHandlerEnabled' is true the default click behavior would be worked.
|
||
var item = ev.item;
|
||
var checked = ev.checked;
|
||
var isSingleSelected = legend.get('selectedMode') === 'single'; // 图例的选中模式
|
||
|
||
var clickedValue = item.dataValue; // import: 需要取该图例项原始的数值
|
||
|
||
if (checked) {
|
||
Util.Array.remove(filterVals, clickedValue);
|
||
|
||
if (self._isFieldInView(field, clickedValue, chart)) {
|
||
chart.filter(field, function (field) {
|
||
return isSingleSelected ? field === clickedValue : !Util.inArray(filterVals, field);
|
||
});
|
||
}
|
||
|
||
Util.each(views, function (view) {
|
||
if (self._isFieldInView(field, clickedValue, view)) {
|
||
view.filter(field, function (field) {
|
||
return isSingleSelected ? field === clickedValue : !Util.inArray(filterVals, field);
|
||
});
|
||
}
|
||
});
|
||
} else if (!isSingleSelected) {
|
||
filterVals.push(clickedValue);
|
||
|
||
if (self._isFieldInView(field, clickedValue, chart)) {
|
||
chart.filter(field, function (field) {
|
||
return !Util.inArray(filterVals, field);
|
||
});
|
||
}
|
||
|
||
Util.each(views, function (view) {
|
||
if (self._isFieldInView(field, clickedValue, view)) {
|
||
view.filter(field, function (field) {
|
||
return !Util.inArray(filterVals, field);
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
if (options.onClick) {
|
||
options.onClick(ev);
|
||
}
|
||
|
||
chart.set('keepLegend', true); // 图例不重新渲染
|
||
|
||
chart.set('keepPadding', true); // 边框不重新计算
|
||
|
||
chart.repaint();
|
||
chart.set('keepPadding', false);
|
||
chart.set('keepLegend', false);
|
||
}
|
||
});
|
||
};
|
||
|
||
_proto._bindClickEventForMix = function _bindClickEventForMix(legend) {
|
||
var self = this;
|
||
var chart = self.chart;
|
||
var geoms = chart.getAllGeoms();
|
||
legend.on('itemclick', function (ev) {
|
||
var itemField = ev.item.field;
|
||
var checked = ev.checked;
|
||
|
||
if (checked) {
|
||
Util.each(geoms, function (geom) {
|
||
var field = geom.getYScale().field;
|
||
|
||
if (field === itemField) {
|
||
geom.show();
|
||
}
|
||
});
|
||
} else {
|
||
Util.each(geoms, function (geom) {
|
||
var field = geom.getYScale().field;
|
||
|
||
if (field === itemField) {
|
||
geom.hide();
|
||
}
|
||
});
|
||
}
|
||
});
|
||
};
|
||
|
||
_proto._filterLabels = function _filterLabels(shape, geom, visible) {
|
||
if (shape.get('gLabel')) {
|
||
shape.get('gLabel').set('visible', visible);
|
||
} else {
|
||
var labelCfg = geom.get('labelCfg');
|
||
|
||
if (labelCfg && labelCfg.fields && labelCfg.fields.length > 0) {
|
||
var xScale = geom.getXScale();
|
||
var yScale = geom.getYScale();
|
||
var xField = xScale.field;
|
||
var yField = yScale.field;
|
||
|
||
var shapeData = shape.get('origin')._origin;
|
||
|
||
var labelContainer = geom.get('labelContainer');
|
||
var labels = labelContainer.get('labelsGroup').get('children');
|
||
Util.each(labels, function (label) {
|
||
var labelData = label.get('origin') || [];
|
||
|
||
if (labelData[xField] === shapeData[xField] && labelData[yField] === shapeData[yField]) {
|
||
label.set('visible', visible);
|
||
shape.set('gLabel', label);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
};
|
||
|
||
_proto._bindFilterEvent = function _bindFilterEvent(legend, scale) {
|
||
var self = this;
|
||
var chart = this.chart;
|
||
var field = scale.field;
|
||
legend.on('itemfilter', function (ev) {
|
||
var range = ev.range;
|
||
chart.filterShape(function (obj, shape, geom) {
|
||
// @2018-12-21 by blue.lb 由于数值0直接被类型转换为false,这里需要做更精确一点的判断
|
||
if (!Util.isNil(obj[field])) {
|
||
var filtered = obj[field] >= range[0] && obj[field] <= range[1]; // shape 带 label,则还需要隐藏 label
|
||
|
||
self._filterLabels(shape, geom, filtered);
|
||
|
||
return filtered;
|
||
}
|
||
|
||
return true;
|
||
});
|
||
var geoms = chart.getAllGeoms() || [];
|
||
|
||
var _loop = function _loop(i) {
|
||
var geom = geoms[i];
|
||
|
||
if (geom.get('type') === 'heatmap') {
|
||
requireAnimationFrameFn(function () {
|
||
geom.drawWithRange(range);
|
||
});
|
||
}
|
||
};
|
||
|
||
for (var i = 0; i < geoms.length; i++) {
|
||
_loop(i);
|
||
}
|
||
});
|
||
};
|
||
|
||
_proto._getShapeData = function _getShapeData(shape) {
|
||
var originData = shape.get('origin');
|
||
|
||
if (Util.isArray(originData)) {
|
||
originData = originData[0];
|
||
}
|
||
|
||
return originData[FIELD_ORIGIN];
|
||
};
|
||
|
||
_proto._bindHoverEvent = function _bindHoverEvent(legend, field) {
|
||
var self = this;
|
||
var chart = self.chart;
|
||
var geoms = chart.getAllGeoms();
|
||
var options = self.options;
|
||
var canvas = chart.get('canvas');
|
||
legend.on('itemhover', function (ev) {
|
||
var value = ev.item.value;
|
||
var pre = self.pre;
|
||
|
||
if (!pre) {
|
||
Util.each(geoms, function (geom) {
|
||
var shapeContainer = geom.get('shapeContainer');
|
||
var shapes = geom.getShapes();
|
||
var activeShapes = [];
|
||
|
||
if (field) {
|
||
var scale = geom.get('scales')[field];
|
||
Util.each(shapes, function (shape) {
|
||
var origin = self._getShapeData(shape);
|
||
|
||
if (origin && _snapEqual(origin[field], value, scale)) {
|
||
activeShapes.push(shape);
|
||
}
|
||
});
|
||
} else if (geom.getYScale().field === value) {
|
||
activeShapes = shapes;
|
||
}
|
||
|
||
if (!Util.isEmpty(activeShapes)) {
|
||
ev.shapes = activeShapes;
|
||
ev.geom = geom;
|
||
|
||
if (options.onHover) {
|
||
options.onHover(ev);
|
||
shapeContainer.sort();
|
||
canvas.draw();
|
||
} else {
|
||
geom.setShapesActived(activeShapes);
|
||
}
|
||
}
|
||
});
|
||
self.pre = value;
|
||
} else if (pre === value) {
|
||
return;
|
||
}
|
||
});
|
||
legend.on('itemunhover', function (ev) {
|
||
self.pre = null;
|
||
|
||
if (options.onUnhover) {
|
||
options.onUnhover(ev);
|
||
}
|
||
|
||
Util.each(geoms, function (geom) {
|
||
if (geom.get('activeShapes')) {
|
||
geom.clearActivedShapes();
|
||
canvas.draw();
|
||
}
|
||
});
|
||
});
|
||
};
|
||
|
||
_proto._isFiltered = function _isFiltered(scale, filterVals, scaleValue) {
|
||
if (!scale.isCategory) {
|
||
return true;
|
||
}
|
||
|
||
var rst = true;
|
||
scaleValue = scale.invert(scaleValue);
|
||
Util.each(filterVals, function (val) {
|
||
if (scale.getText(val) === scale.getText(scaleValue)) {
|
||
rst = false;
|
||
return false;
|
||
}
|
||
});
|
||
return rst;
|
||
};
|
||
|
||
_proto._alignLegend = function _alignLegend(legend, pre, region, position) {
|
||
var self = this;
|
||
var viewTheme = self.viewTheme;
|
||
var container = self.container;
|
||
var canvas = container.get('canvas');
|
||
var width = canvas.get('width');
|
||
var height = canvas.get('height');
|
||
var totalRegion = self.totalRegion;
|
||
var plotRange = self.plotRange;
|
||
var backRange = self.getBackRange(); // 背景占得范围
|
||
|
||
var offsetX = legend.get('offset')[0] || 0;
|
||
var offsetY = legend.get('offset')[1] || 0; // const offset = Util.isNil(legend.get('offset')) ? MARGIN : legend.get('offset');
|
||
|
||
var legendHeight = legend.getHeight();
|
||
var legendWidth = legend.getWidth();
|
||
var borderMargin = viewTheme.legend.margin;
|
||
var innerMargin = viewTheme.legend.legendMargin;
|
||
var legendNum = self.legends[position].length;
|
||
var posArray = position.split('-');
|
||
var x = 0;
|
||
var y = 0;
|
||
var tempoRegion = legendNum > 1 ? totalRegion : region;
|
||
|
||
if (posArray[0] === 'left' || posArray[0] === 'right') {
|
||
height = plotRange.br.y;
|
||
x = self._getXAlign(posArray[0], width, region, backRange, legendWidth, borderMargin);
|
||
|
||
if (pre) {
|
||
// @2018-10-19 by blue.lb 由于legend中并不存在y属性,这里需要先获取group再获取y值
|
||
// @2019-03-21 by blue.lb 由于内部实现问题,usehtml部分的实例可以直接获取x、y的
|
||
y = (pre.get('y') || pre.get('group').get('y')) + pre.getHeight() + innerMargin;
|
||
} else {
|
||
y = self._getYAlignVertical(posArray[1], height, tempoRegion, backRange, 0, borderMargin, canvas.get('height'));
|
||
}
|
||
} else if (posArray[0] === 'top' || posArray[0] === 'bottom') {
|
||
y = self._getYAlignHorizontal(posArray[0], height, region, backRange, legendHeight, borderMargin);
|
||
|
||
if (pre) {
|
||
var preWidth = pre.getWidth(); // @2018-10-19 by blue.lb 由于legend中并不存在x属性,这里需要先获取group再获取x值
|
||
// @2019-03-21 by blue.lb 由于内部实现问题,usehtml部分的实例可以直接获取x、y的
|
||
|
||
x = (pre.get('x') || pre.get('group').get('x')) + preWidth + innerMargin;
|
||
} else {
|
||
x = self._getXAlign(posArray[1], width, tempoRegion, backRange, 0, borderMargin);
|
||
if (posArray[1] === 'right') x = plotRange.br.x - tempoRegion.totalWidth;
|
||
}
|
||
}
|
||
|
||
legend.move(x + offsetX, y + offsetY);
|
||
};
|
||
|
||
_proto._getXAlign = function _getXAlign(pos, width, region, backRange, legendWidth, borderMargin) {
|
||
var leftPos = backRange.minX - legendWidth - borderMargin[3] < 0 ? 0 : backRange.minX - legendWidth - borderMargin[3];
|
||
var x = pos === 'left' ? leftPos : backRange.maxX + borderMargin[1];
|
||
|
||
if (pos === 'center') {
|
||
x = (width - region.totalWidth) / 2;
|
||
}
|
||
|
||
return x;
|
||
};
|
||
|
||
_proto._getYAlignHorizontal = function _getYAlignHorizontal(pos, height, region, backRange, legendHeight, borderMargin) {
|
||
var y = pos === 'top' ? backRange.minY - legendHeight - borderMargin[0] : backRange.maxY + borderMargin[2];
|
||
return y;
|
||
};
|
||
|
||
_proto._getYAlignVertical = function _getYAlignVertical(pos, height, region, backRange, legendHeight, borderMargin, canvasHeight) {
|
||
var y = pos === 'top' ? backRange.minY - legendHeight - borderMargin[0] : height - region.totalHeight;
|
||
|
||
if (pos === 'center') {
|
||
y = (canvasHeight - region.totalHeight) / 2;
|
||
}
|
||
|
||
return y;
|
||
};
|
||
|
||
_proto._getSubRegion = function _getSubRegion(legends) {
|
||
var maxWidth = 0;
|
||
var maxHeight = 0;
|
||
var totalWidth = 0;
|
||
var totalHeight = 0;
|
||
Util.each(legends, function (legend) {
|
||
var width = legend.getWidth();
|
||
var height = legend.getHeight();
|
||
|
||
if (maxWidth < width) {
|
||
maxWidth = width;
|
||
}
|
||
|
||
totalWidth += width;
|
||
|
||
if (maxHeight < height) {
|
||
maxHeight = height;
|
||
}
|
||
|
||
totalHeight += height;
|
||
});
|
||
return {
|
||
maxWidth: maxWidth,
|
||
totalWidth: totalWidth,
|
||
maxHeight: maxHeight,
|
||
totalHeight: totalHeight
|
||
};
|
||
};
|
||
|
||
_proto._getRegion = function _getRegion() {
|
||
var self = this;
|
||
var viewTheme = self.viewTheme;
|
||
var legends = self.legends;
|
||
var innerMargin = viewTheme.legend.legendMargin;
|
||
var subs = [];
|
||
var totalWidth = 0;
|
||
var totalHeight = 0;
|
||
Util.each(legends, function (legendItems) {
|
||
var subRegion = self._getSubRegion(legendItems);
|
||
|
||
subs.push(subRegion);
|
||
totalWidth += subRegion.totalWidth + innerMargin;
|
||
totalHeight += subRegion.totalHeight + innerMargin;
|
||
});
|
||
return {
|
||
totalWidth: totalWidth,
|
||
totalHeight: totalHeight,
|
||
subs: subs
|
||
};
|
||
};
|
||
|
||
_proto._addCategoryLegend = function _addCategoryLegend(scale, attr, geom, filterVals, position) {
|
||
var self = this;
|
||
var field = scale.field;
|
||
var legendOptions = self.options;
|
||
var fieldOption = legendOptions[field];
|
||
|
||
if (fieldOption) {
|
||
legendOptions = fieldOption;
|
||
}
|
||
|
||
var legends = self.legends;
|
||
legends[position] = legends[position] || [];
|
||
var container = self.container;
|
||
var items = [];
|
||
var ticks = scale.getTicks();
|
||
var isByAttr = true;
|
||
var shapeType = geom.get('shapeType') || 'point';
|
||
var shape = geom.getDefaultValue('shape') || 'circle';
|
||
|
||
if (legendOptions[field] && legendOptions[field].marker) {
|
||
// 用户为 field 对应的图例定义了 marker
|
||
shape = legendOptions[field].marker;
|
||
shapeType = 'point';
|
||
isByAttr = false;
|
||
} else if (legendOptions.marker) {
|
||
shape = legendOptions.marker;
|
||
shapeType = 'point';
|
||
isByAttr = false;
|
||
}
|
||
|
||
var chart = self.chart;
|
||
var viewTheme = self.viewTheme;
|
||
var canvas = chart.get('canvas');
|
||
var plotRange = self.plotRange;
|
||
var posArray = position.split('-');
|
||
var maxLength = posArray[0] === 'right' || posArray[0] === 'left' ? plotRange.bl.y - plotRange.tr.y : canvas.get('width');
|
||
Util.each(ticks, function (tick) {
|
||
var text = tick.text;
|
||
var name = text;
|
||
var scaleValue = tick.value;
|
||
var value = scale.invert(scaleValue);
|
||
var cfg = {
|
||
isInCircle: geom.isInCircle()
|
||
};
|
||
var checked = filterVals ? self._isFiltered(scale, filterVals, scaleValue) : true;
|
||
var colorAttr = geom.getAttr('color');
|
||
var shapeAttr = geom.getAttr('shape');
|
||
|
||
if (colorAttr) {
|
||
// 存在颜色映射
|
||
if (colorAttr.callback && colorAttr.callback.length > 1) {
|
||
// 多参数映射,阻止程序报错
|
||
var restArgs = Array(colorAttr.callback.length - 1).fill('');
|
||
cfg.color = colorAttr.mapping.apply(colorAttr, [value].concat(restArgs)).join('') || viewTheme.defaultColor;
|
||
} else {
|
||
cfg.color = colorAttr.mapping(value).join('') || viewTheme.defaultColor;
|
||
}
|
||
}
|
||
|
||
if (isByAttr && shapeAttr) {
|
||
// 存在形状映射
|
||
if (shapeAttr.callback && shapeAttr.callback.length > 1) {
|
||
// 多参数映射,阻止程序报错
|
||
var _restArgs = Array(shapeAttr.callback.length - 1).fill('');
|
||
|
||
shape = shapeAttr.mapping.apply(shapeAttr, [value].concat(_restArgs)).join('');
|
||
} else {
|
||
shape = shapeAttr.mapping(value).join('');
|
||
}
|
||
}
|
||
|
||
var shapeObject = Shape.getShapeFactory(shapeType);
|
||
var marker = shapeObject.getMarkerCfg(shape, cfg);
|
||
|
||
if (Global.legendMarkerRadius) {
|
||
marker.radius = Global.legendMarkerRadius;
|
||
}
|
||
|
||
if (Util.isFunction(shape)) {
|
||
marker.symbol = shape;
|
||
}
|
||
|
||
items.push({
|
||
value: name,
|
||
// 图例项显示文本的内容
|
||
dataValue: value,
|
||
// 图例项对应原始数据中的数值
|
||
checked: checked,
|
||
marker: marker
|
||
});
|
||
});
|
||
var legendCfg = Util.deepMix({}, viewTheme.legend[posArray[0]], legendOptions[field] || legendOptions, {
|
||
viewId: chart.get('_id'),
|
||
maxLength: maxLength,
|
||
items: items,
|
||
container: container,
|
||
position: [0, 0]
|
||
});
|
||
|
||
if (legendCfg.title) {
|
||
Util.deepMix(legendCfg, {
|
||
title: {
|
||
text: scale.alias || scale.field
|
||
}
|
||
});
|
||
}
|
||
|
||
var legend;
|
||
|
||
if (self._isTailLegend(legendOptions, geom)) {
|
||
legendCfg.chart = self.chart;
|
||
legendCfg.geom = geom;
|
||
legend = new Tail(legendCfg);
|
||
} else {
|
||
if (legendOptions.useHtml) {
|
||
var canvasEle = container.get('canvas').get('el');
|
||
container = legendOptions.container;
|
||
|
||
if (Util.isString(container) && /^\#/.test(container)) {
|
||
// 如果传入 dom 节点的 id
|
||
var id = container.replace('#', '');
|
||
container = document.getElementById(id);
|
||
}
|
||
|
||
if (!container) {
|
||
container = canvasEle.parentNode;
|
||
}
|
||
|
||
legendCfg.container = container;
|
||
if (legendCfg.legendStyle === undefined) legendCfg.legendStyle = {};
|
||
legendCfg.legendStyle.CONTAINER_CLASS = _extends({}, legendCfg.legendStyle.CONTAINER_CLASS, {
|
||
position: 'absolute',
|
||
overflow: 'auto',
|
||
'z-index': canvasEle.style.zIndex === '' ? 1 : parseInt(canvasEle.style.zIndex, 10) + 1
|
||
});
|
||
|
||
if (legendOptions.flipPage) {
|
||
legendCfg.legendStyle.CONTAINER_CLASS.height = posArray[0] === 'right' || posArray[0] === 'left' ? maxLength + 'px' : 'auto';
|
||
legendCfg.legendStyle.CONTAINER_CLASS.width = !(posArray[0] === 'right' || posArray[0] === 'left') ? maxLength + 'px' : 'auto';
|
||
legend = new Legend.CatPageHtml(legendCfg);
|
||
} else {
|
||
legend = new Legend.CatHtml(legendCfg);
|
||
}
|
||
} else {
|
||
legend = new Legend.Category(legendCfg);
|
||
}
|
||
}
|
||
|
||
self._bindClickEvent(legend, scale, filterVals);
|
||
|
||
legends[position].push(legend);
|
||
return legend;
|
||
};
|
||
|
||
_proto._bindChartMove = function _bindChartMove(scale) {
|
||
var chart = this.chart;
|
||
var legends = this.legends;
|
||
chart.on('plotmove', function (ev) {
|
||
var selected = false;
|
||
|
||
if (ev.target) {
|
||
var origin = ev.target.get('origin');
|
||
|
||
if (origin) {
|
||
var data = origin[FIELD_ORIGIN] || origin[0][FIELD_ORIGIN];
|
||
var field = scale.field;
|
||
|
||
if (data) {
|
||
var value = data[field];
|
||
Util.each(legends, function (legendItems) {
|
||
Util.each(legendItems, function (legend) {
|
||
selected = true;
|
||
!legend.destroyed && legend.activate(value);
|
||
});
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!selected) {
|
||
Util.each(legends, function (legendItems) {
|
||
Util.each(legendItems, function (legend) {
|
||
!legend.destroyed && legend.deactivate();
|
||
});
|
||
});
|
||
}
|
||
});
|
||
};
|
||
|
||
_proto._addContinuousLegend = function _addContinuousLegend(scale, attr, position) {
|
||
var self = this;
|
||
var legends = self.legends;
|
||
legends[position] = legends[position] || [];
|
||
var container = self.container;
|
||
var field = scale.field;
|
||
var ticks = scale.getTicks();
|
||
var items = [];
|
||
var legend;
|
||
var minValue;
|
||
var maxValue;
|
||
var viewTheme = self.viewTheme;
|
||
Util.each(ticks, function (tick) {
|
||
var scaleValue = tick.value;
|
||
var invertValue = scale.invert(scaleValue);
|
||
var attrValue = attr.mapping(invertValue).join('');
|
||
items.push({
|
||
value: tick.tickValue,
|
||
// tick.text
|
||
attrValue: attrValue,
|
||
color: attrValue,
|
||
scaleValue: scaleValue
|
||
});
|
||
|
||
if (scaleValue === 0) {
|
||
minValue = true;
|
||
}
|
||
|
||
if (scaleValue === 1) {
|
||
maxValue = true;
|
||
}
|
||
});
|
||
|
||
if (!minValue) {
|
||
items.push({
|
||
value: scale.min,
|
||
attrValue: attr.mapping(0).join(''),
|
||
color: attr.mapping(0).join(''),
|
||
scaleValue: 0
|
||
});
|
||
}
|
||
|
||
if (!maxValue) {
|
||
items.push({
|
||
value: scale.max,
|
||
attrValue: attr.mapping(1).join(''),
|
||
color: attr.mapping(1).join(''),
|
||
scaleValue: 1
|
||
});
|
||
}
|
||
|
||
var options = self.options;
|
||
var posArray = position.split('-');
|
||
var defaultCfg = viewTheme.legend[posArray[0]];
|
||
|
||
if (options && options.slidable === false || options[field] && options[field].slidable === false) {
|
||
defaultCfg = Util.mix({}, defaultCfg, viewTheme.legend.gradient);
|
||
}
|
||
|
||
var legendCfg = Util.deepMix({}, defaultCfg, options[field] || options, {
|
||
items: items,
|
||
attr: attr,
|
||
formatter: scale.formatter,
|
||
container: container,
|
||
position: [0, 0]
|
||
});
|
||
|
||
if (legendCfg.title) {
|
||
Util.deepMix(legendCfg, {
|
||
title: {
|
||
text: scale.alias || scale.field
|
||
}
|
||
});
|
||
}
|
||
|
||
if (attr.type === 'color') {
|
||
legend = new Legend.Color(legendCfg);
|
||
} else if (attr.type === 'size') {
|
||
if (options && options.sizeType === 'circle') legend = new Legend.CircleSize(legendCfg);else legend = new Legend.Size(legendCfg);
|
||
} else {
|
||
return;
|
||
}
|
||
|
||
self._bindFilterEvent(legend, scale);
|
||
|
||
legends[position].push(legend);
|
||
return legend;
|
||
};
|
||
|
||
_proto._isTailLegend = function _isTailLegend(opt, geom) {
|
||
if (opt.hasOwnProperty('attachLast') && opt.attachLast) {
|
||
var geomType = geom.get('type');
|
||
if (geomType === 'line' || geomType === 'lineStack' || geomType === 'area' || geomType === 'areaStack') return true;
|
||
}
|
||
|
||
return false;
|
||
};
|
||
|
||
_proto._adjustPosition = function _adjustPosition(position, isTailLegend) {
|
||
var pos;
|
||
|
||
if (isTailLegend) {
|
||
pos = 'right-top';
|
||
} else if (Util.isArray(position)) {
|
||
pos = String(position[0]) + '-' + String(position[1]);
|
||
} else {
|
||
var posArr = position.split('-');
|
||
|
||
if (posArr.length === 1) {
|
||
// 只用了left/right/bottom/top一个位置定位
|
||
if (posArr[0] === 'left') pos = 'left-bottom';
|
||
if (posArr[0] === 'right') pos = 'right-bottom';
|
||
if (posArr[0] === 'top') pos = 'top-center';
|
||
if (posArr[0] === 'bottom') pos = 'bottom-center';
|
||
} else {
|
||
pos = position;
|
||
}
|
||
}
|
||
|
||
return pos;
|
||
};
|
||
|
||
_proto.addLegend = function addLegend(scale, attr, geom, filterVals) {
|
||
var self = this;
|
||
var legendOptions = self.options;
|
||
var field = scale.field;
|
||
var fieldOption = legendOptions[field];
|
||
var viewTheme = self.viewTheme;
|
||
|
||
if (fieldOption === false) {
|
||
// 如果不显示此图例
|
||
return null;
|
||
}
|
||
|
||
if (fieldOption && fieldOption.custom) {
|
||
self.addCustomLegend(field);
|
||
} else {
|
||
var position = legendOptions.position || viewTheme.defaultLegendPosition;
|
||
position = self._adjustPosition(position, self._isTailLegend(legendOptions, geom));
|
||
|
||
if (fieldOption && fieldOption.position) {
|
||
// 如果对某个图例单独设置 position,则对 position 重新赋值
|
||
position = self._adjustPosition(fieldOption.position, self._isTailLegend(fieldOption, geom));
|
||
}
|
||
|
||
var legend;
|
||
|
||
if (scale.isLinear) {
|
||
legend = self._addContinuousLegend(scale, attr, position);
|
||
} else {
|
||
legend = self._addCategoryLegend(scale, attr, geom, filterVals, position);
|
||
}
|
||
|
||
if (legend) {
|
||
self._bindHoverEvent(legend, field);
|
||
|
||
legendOptions.reactive && self._bindChartMove(scale);
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* 自定义图例
|
||
* @param {string} field 自定义图例的数据字段名,可以为空
|
||
* @return {object} legend 自定义图例实例
|
||
*/
|
||
;
|
||
|
||
_proto.addCustomLegend = function addCustomLegend(field) {
|
||
var self = this;
|
||
var chart = self.chart;
|
||
var viewTheme = self.viewTheme;
|
||
var container = self.container;
|
||
var legendOptions = self.options;
|
||
|
||
if (field) {
|
||
legendOptions = legendOptions[field];
|
||
}
|
||
|
||
var position = legendOptions.position || viewTheme.defaultLegendPosition;
|
||
position = self._adjustPosition(position);
|
||
var legends = self.legends;
|
||
legends[position] = legends[position] || [];
|
||
var items = legendOptions.items;
|
||
|
||
if (!items) {
|
||
return;
|
||
}
|
||
|
||
var geoms = chart.getAllGeoms();
|
||
Util.each(items, function (item) {
|
||
var geom = findGeom(geoms, item.value);
|
||
|
||
if (!Util.isPlainObject(item.marker)) {
|
||
// 直接传入字符串或者回调函数时转换为对象,如 item.marker = 'circle'
|
||
item.marker = {
|
||
symbol: item.marker || 'circle',
|
||
radius: Global.legendMarkerRadius || MARKER_SIZE
|
||
};
|
||
|
||
if (Util.indexOf(STROKE_MARKERS, item.marker.symbol) !== -1) {
|
||
item.marker.stroke = item.fill;
|
||
} else {
|
||
item.marker.fill = item.fill;
|
||
}
|
||
} else {
|
||
// 用户传入对象 item.marker = { symbol: 'circle', fill: 'red', radius: 3 }
|
||
item.marker.radius = item.marker.radius || Global.legendMarkerRadius || MARKER_SIZE;
|
||
}
|
||
|
||
var symbol = item.marker.symbol;
|
||
|
||
if (Util.isString(symbol) && symbol.indexOf('hollow') !== -1) {
|
||
item.marker.symbol = Util.lowerFirst(symbol.substr(6));
|
||
}
|
||
|
||
item.checked = Util.isNil(item.checked) ? true : item.checked;
|
||
item.geom = geom;
|
||
});
|
||
var canvas = chart.get('canvas');
|
||
var plotRange = self.plotRange;
|
||
var posArray = position.split('-');
|
||
var maxLength = posArray[0] === 'right' || posArray[0] === 'left' ? plotRange.bl.y - plotRange.tr.y : canvas.get('width');
|
||
var legendCfg = Util.deepMix({}, viewTheme.legend[posArray[0]], legendOptions, {
|
||
maxLength: maxLength,
|
||
items: items,
|
||
container: container,
|
||
position: [0, 0]
|
||
});
|
||
var legend;
|
||
|
||
if (legendOptions.useHtml) {
|
||
var htmlContainer = legendOptions.container;
|
||
|
||
if (/^\#/.test(container)) {
|
||
// 如果传入 dom 节点的 id
|
||
var id = htmlContainer.replace('#', '');
|
||
htmlContainer = document.getElementById(id);
|
||
} else if (!htmlContainer) {
|
||
htmlContainer = container.get('canvas').get('el').parentNode;
|
||
}
|
||
|
||
legendCfg.container = htmlContainer;
|
||
if (legendCfg.legendStyle === undefined) legendCfg.legendStyle = {};
|
||
|
||
if (!legendCfg.legendStyle.CONTAINER_CLASS) {
|
||
legendCfg.legendStyle.CONTAINER_CLASS = {
|
||
height: posArray[0] === 'right' || posArray[0] === 'left' ? maxLength + 'px' : 'auto',
|
||
width: !(posArray[0] === 'right' || posArray[0] === 'left') ? maxLength + 'px' : 'auto',
|
||
position: 'absolute',
|
||
overflow: 'auto'
|
||
};
|
||
}
|
||
|
||
if (legendOptions.flipPage) legend = new Legend.CatPageHtml(legendCfg);else legend = new Legend.CatHtml(legendCfg);
|
||
} else legend = new Legend.Category(legendCfg);
|
||
|
||
legends[position].push(legend);
|
||
legend.on('itemclick', function (ev) {
|
||
if (legendOptions.onClick) {
|
||
// 用户自定义了图例点击事件
|
||
legendOptions.onClick(ev);
|
||
}
|
||
});
|
||
|
||
self._bindHoverEvent(legend);
|
||
|
||
return legend;
|
||
};
|
||
|
||
_proto.addMixedLegend = function addMixedLegend(scales, geoms) {
|
||
var self = this;
|
||
var legendOptions = self.options;
|
||
var items = [];
|
||
Util.each(scales, function (scale) {
|
||
var value = scale.alias || scale.field;
|
||
var fieldLegendOptions = legendOptions[scale.field];
|
||
Util.each(geoms, function (geom) {
|
||
if (geom.getYScale() === scale && scale.values && scale.values.length > 0 && fieldLegendOptions !== false) {
|
||
var shapeType = geom.get('shapeType') || 'point';
|
||
var shape = geom.getDefaultValue('shape') || 'circle';
|
||
var shapeObject = Shape.getShapeFactory(shapeType);
|
||
var cfg = {
|
||
color: geom.getDefaultValue('color')
|
||
};
|
||
var marker = shapeObject.getMarkerCfg(shape, cfg);
|
||
|
||
if (Global.legendMarkerRadius) {
|
||
marker.radius = Global.legendMarkerRadius;
|
||
}
|
||
|
||
var item = {
|
||
value: value,
|
||
marker: marker,
|
||
field: scale.field
|
||
};
|
||
items.push(item);
|
||
}
|
||
}); // end of geom loop
|
||
}); // end of scale loop
|
||
|
||
var options = {
|
||
custom: true,
|
||
items: items
|
||
};
|
||
self.options = Util.deepMix({}, options, self.options);
|
||
var legend = self.addCustomLegend();
|
||
|
||
self._bindClickEventForMix(legend);
|
||
};
|
||
|
||
_proto.alignLegends = function alignLegends() {
|
||
var self = this;
|
||
var legends = self.legends;
|
||
|
||
var totalRegion = self._getRegion(legends);
|
||
|
||
self.totalRegion = totalRegion;
|
||
var i = 0;
|
||
Util.each(legends, function (legendItems, position) {
|
||
var region =
|
||
/* self._getRegion(legendItems)*/
|
||
totalRegion.subs[i];
|
||
Util.each(legendItems, function (legend, index) {
|
||
var pre = legendItems[index - 1];
|
||
|
||
if (!(legend.get('useHtml') && !legend.get('autoPosition'))) {
|
||
self._alignLegend(legend, pre, region, position);
|
||
}
|
||
});
|
||
i++;
|
||
});
|
||
return this;
|
||
};
|
||
|
||
return LegendController;
|
||
}();
|
||
|
||
module.exports = LegendController; |