You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1706 lines
69 KiB
1706 lines
69 KiB
4 years ago
|
/**
|
||
|
* echarts图表基类
|
||
|
*
|
||
|
* @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
|
||
|
* @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
|
||
|
*
|
||
|
*/
|
||
|
define(function (require) {
|
||
|
// 图形依赖
|
||
|
var ImageShape = require('zrender/shape/Image');
|
||
|
var IconShape = require('../util/shape/Icon');
|
||
|
var MarkLineShape = require('../util/shape/MarkLine');
|
||
|
var SymbolShape = require('../util/shape/Symbol');
|
||
|
var PolylineShape = require('zrender/shape/Polyline');
|
||
|
var ShapeBundle = require('zrender/shape/ShapeBundle');
|
||
|
|
||
|
var ecConfig = require('../config');
|
||
|
var ecData = require('../util/ecData');
|
||
|
var ecAnimation = require('../util/ecAnimation');
|
||
|
var ecEffect = require('../util/ecEffect');
|
||
|
var accMath = require('../util/accMath');
|
||
|
var ComponentBase = require('../component/base');
|
||
|
var EdgeBundling = require('../layout/EdgeBundling');
|
||
|
|
||
|
var zrUtil = require('zrender/tool/util');
|
||
|
var zrArea = require('zrender/tool/area');
|
||
|
|
||
|
// Some utility functions
|
||
|
function isCoordAvailable(coord) {
|
||
|
return coord.x != null && coord.y != null;
|
||
|
}
|
||
|
|
||
|
function Base(ecTheme, messageCenter, zr, option, myChart) {
|
||
|
|
||
|
ComponentBase.call(this, ecTheme, messageCenter, zr, option, myChart);
|
||
|
|
||
|
var self = this;
|
||
|
this.selectedMap = {};
|
||
|
this.lastShapeList = [];
|
||
|
this.shapeHandler = {
|
||
|
onclick: function () {
|
||
|
self.isClick = true;
|
||
|
},
|
||
|
|
||
|
ondragover: function (param) {
|
||
|
// 返回触发可计算特性的图形提示
|
||
|
var calculableShape = param.target;
|
||
|
calculableShape.highlightStyle = calculableShape.highlightStyle || {};
|
||
|
|
||
|
// 备份特出特性
|
||
|
var highlightStyle = calculableShape.highlightStyle;
|
||
|
var brushType = highlightStyle.brushTyep;
|
||
|
var strokeColor = highlightStyle.strokeColor;
|
||
|
var lineWidth = highlightStyle.lineWidth;
|
||
|
|
||
|
highlightStyle.brushType = 'stroke';
|
||
|
highlightStyle.strokeColor = self.ecTheme.calculableColor
|
||
|
|| ecConfig.calculableColor;
|
||
|
highlightStyle.lineWidth = calculableShape.type === 'icon' ? 30 : 10;
|
||
|
|
||
|
self.zr.addHoverShape(calculableShape);
|
||
|
|
||
|
setTimeout(function (){
|
||
|
// 复位
|
||
|
if (highlightStyle) {
|
||
|
highlightStyle.brushType = brushType;
|
||
|
highlightStyle.strokeColor = strokeColor;
|
||
|
highlightStyle.lineWidth = lineWidth;
|
||
|
}
|
||
|
},20);
|
||
|
},
|
||
|
|
||
|
ondrop: function (param) {
|
||
|
// 排除一些非数据的拖拽进入
|
||
|
if (ecData.get(param.dragged, 'data') != null) {
|
||
|
self.isDrop = true;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
ondragend: function () {
|
||
|
self.isDragend = true;
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 基类方法
|
||
|
*/
|
||
|
Base.prototype = {
|
||
|
/**
|
||
|
* 图形拖拽特性
|
||
|
*/
|
||
|
setCalculable: function (shape) {
|
||
|
shape.dragEnableTime = this.ecTheme.DRAG_ENABLE_TIME || ecConfig.DRAG_ENABLE_TIME;
|
||
|
shape.ondragover = this.shapeHandler.ondragover;
|
||
|
shape.ondragend = this.shapeHandler.ondragend;
|
||
|
shape.ondrop = this.shapeHandler.ondrop;
|
||
|
return shape;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 数据项被拖拽进来
|
||
|
*/
|
||
|
ondrop: function (param, status) {
|
||
|
if (!this.isDrop || !param.target || status.dragIn) {
|
||
|
// 没有在当前实例上发生拖拽行为或者已经被认领了则直接返回
|
||
|
return;
|
||
|
}
|
||
|
var target = param.target; // 拖拽安放目标
|
||
|
var dragged = param.dragged; // 当前被拖拽的图形对象
|
||
|
|
||
|
var seriesIndex = ecData.get(target, 'seriesIndex');
|
||
|
var dataIndex = ecData.get(target, 'dataIndex');
|
||
|
|
||
|
var series = this.series;
|
||
|
var data;
|
||
|
var legend = this.component.legend;
|
||
|
if (dataIndex === -1) {
|
||
|
// 落到calculableCase上,数据被拖拽进某个饼图|雷达|漏斗,增加数据
|
||
|
if (ecData.get(dragged, 'seriesIndex') == seriesIndex) {
|
||
|
// 自己拖拽到自己
|
||
|
status.dragOut = status.dragIn = status.needRefresh = true;
|
||
|
this.isDrop = false;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
data = {
|
||
|
value: ecData.get(dragged, 'value'),
|
||
|
name: ecData.get(dragged, 'name')
|
||
|
};
|
||
|
|
||
|
// 修饼图数值不为负值
|
||
|
if (this.type === ecConfig.CHART_TYPE_PIE && data.value < 0) {
|
||
|
data.value = 0;
|
||
|
}
|
||
|
|
||
|
var hasFind = false;
|
||
|
var sData = series[seriesIndex].data;
|
||
|
for (var i = 0, l = sData.length; i < l; i++) {
|
||
|
if (sData[i].name === data.name && sData[i].value === '-') {
|
||
|
series[seriesIndex].data[i].value = data.value;
|
||
|
hasFind = true;
|
||
|
}
|
||
|
}
|
||
|
!hasFind && series[seriesIndex].data.push(data);
|
||
|
|
||
|
legend && legend.add(
|
||
|
data.name,
|
||
|
dragged.style.color || dragged.style.strokeColor
|
||
|
);
|
||
|
}
|
||
|
else {
|
||
|
// 落到数据item上,数据被拖拽到某个数据项上,数据修改
|
||
|
data = series[seriesIndex].data[dataIndex] || '-';
|
||
|
if (data.value != null) {
|
||
|
if (data.value != '-') {
|
||
|
series[seriesIndex].data[dataIndex].value =
|
||
|
accMath.accAdd(
|
||
|
series[seriesIndex].data[dataIndex].value,
|
||
|
ecData.get(dragged, 'value')
|
||
|
);
|
||
|
}
|
||
|
else {
|
||
|
series[seriesIndex].data[dataIndex].value =
|
||
|
ecData.get(dragged, 'value');
|
||
|
}
|
||
|
|
||
|
if (this.type === ecConfig.CHART_TYPE_FUNNEL
|
||
|
|| this.type === ecConfig.CHART_TYPE_PIE
|
||
|
) {
|
||
|
legend && legend.getRelatedAmount(data.name) === 1
|
||
|
&& this.component.legend.del(data.name);
|
||
|
data.name += this.option.nameConnector + ecData.get(dragged, 'name');
|
||
|
legend && legend.add(
|
||
|
data.name,
|
||
|
dragged.style.color || dragged.style.strokeColor
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if (data != '-') {
|
||
|
series[seriesIndex].data[dataIndex] =
|
||
|
accMath.accAdd(
|
||
|
series[seriesIndex].data[dataIndex],
|
||
|
ecData.get(dragged, 'value')
|
||
|
);
|
||
|
}
|
||
|
else {
|
||
|
series[seriesIndex].data[dataIndex] =
|
||
|
ecData.get(dragged, 'value');
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 别status = {}赋值啊!!
|
||
|
status.dragIn = status.dragIn || true;
|
||
|
|
||
|
// 处理完拖拽事件后复位
|
||
|
this.isDrop = false;
|
||
|
|
||
|
var self = this;
|
||
|
setTimeout(function(){
|
||
|
self.zr.trigger('mousemove', param.event);
|
||
|
}, 300);
|
||
|
|
||
|
return;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 数据项被拖拽出去
|
||
|
*/
|
||
|
ondragend: function (param, status) {
|
||
|
if (!this.isDragend || !param.target || status.dragOut) {
|
||
|
// 没有在当前实例上发生拖拽行为或者已经被认领了则直接返回
|
||
|
return;
|
||
|
}
|
||
|
var target = param.target; // 被拖拽图形元素
|
||
|
|
||
|
var seriesIndex = ecData.get(target, 'seriesIndex');
|
||
|
var dataIndex = ecData.get(target, 'dataIndex');
|
||
|
|
||
|
var series = this.series;
|
||
|
|
||
|
// 删除被拖拽走的数据
|
||
|
if (series[seriesIndex].data[dataIndex].value != null) {
|
||
|
series[seriesIndex].data[dataIndex].value = '-';
|
||
|
// 清理可能有且唯一的legend data
|
||
|
var name = series[seriesIndex].data[dataIndex].name;
|
||
|
var legend = this.component.legend;
|
||
|
if (legend && legend.getRelatedAmount(name) === 0) {
|
||
|
legend.del(name);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
series[seriesIndex].data[dataIndex] = '-';
|
||
|
}
|
||
|
|
||
|
// 别status = {}赋值啊!!
|
||
|
status.dragOut = true;
|
||
|
status.needRefresh = true;
|
||
|
|
||
|
// 处理完拖拽事件后复位
|
||
|
this.isDragend = false;
|
||
|
|
||
|
return;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 图例选择
|
||
|
*/
|
||
|
onlegendSelected: function (param, status) {
|
||
|
var legendSelected = param.selected;
|
||
|
for (var itemName in this.selectedMap) {
|
||
|
if (this.selectedMap[itemName] != legendSelected[itemName]) {
|
||
|
// 有一项不一致都需要重绘
|
||
|
status.needRefresh = true;
|
||
|
}
|
||
|
this.selectedMap[itemName] = legendSelected[itemName];
|
||
|
}
|
||
|
return;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 折线图、柱形图公用方法
|
||
|
*/
|
||
|
_buildPosition: function() {
|
||
|
this._symbol = this.option.symbolList;
|
||
|
this._sIndex2ShapeMap = {}; // series拐点图形类型,seriesIndex索引到shape type
|
||
|
this._sIndex2ColorMap = {}; // series默认颜色索引,seriesIndex索引到color
|
||
|
|
||
|
this.selectedMap = {};
|
||
|
this.xMarkMap = {};
|
||
|
|
||
|
var series = this.series;
|
||
|
// 水平垂直双向series索引 ,position索引到seriesIndex
|
||
|
var _position2sIndexMap = {
|
||
|
top: [],
|
||
|
bottom: [],
|
||
|
left: [],
|
||
|
right: [],
|
||
|
other: []
|
||
|
};
|
||
|
var xAxisIndex;
|
||
|
var yAxisIndex;
|
||
|
var xAxis;
|
||
|
var yAxis;
|
||
|
for (var i = 0, l = series.length; i < l; i++) {
|
||
|
if (series[i].type === this.type) {
|
||
|
series[i] = this.reformOption(series[i]);
|
||
|
this.legendHoverLink = series[i].legendHoverLink || this.legendHoverLink;
|
||
|
xAxisIndex = series[i].xAxisIndex;
|
||
|
yAxisIndex = series[i].yAxisIndex;
|
||
|
xAxis = this.component.xAxis.getAxis(xAxisIndex);
|
||
|
yAxis = this.component.yAxis.getAxis(yAxisIndex);
|
||
|
if (xAxis.type === ecConfig.COMPONENT_TYPE_AXIS_CATEGORY) {
|
||
|
_position2sIndexMap[xAxis.getPosition()].push(i);
|
||
|
}
|
||
|
else if (yAxis.type === ecConfig.COMPONENT_TYPE_AXIS_CATEGORY) {
|
||
|
_position2sIndexMap[yAxis.getPosition()].push(i);
|
||
|
}
|
||
|
else {
|
||
|
_position2sIndexMap.other.push(i);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// console.log(_position2sIndexMap);
|
||
|
for (var position in _position2sIndexMap) {
|
||
|
if (_position2sIndexMap[position].length > 0) {
|
||
|
this._buildSinglePosition(
|
||
|
position, _position2sIndexMap[position]
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this.addShapeList();
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 构建单个方向上的折线图、柱形图公用方法
|
||
|
*
|
||
|
* @param {number} seriesIndex 系列索引
|
||
|
*/
|
||
|
_buildSinglePosition: function (position, seriesArray) {
|
||
|
var mapData = this._mapData(seriesArray);
|
||
|
var locationMap = mapData.locationMap;
|
||
|
var maxDataLength = mapData.maxDataLength;
|
||
|
|
||
|
if (maxDataLength === 0 || locationMap.length === 0) {
|
||
|
return;
|
||
|
}
|
||
|
switch (position) {
|
||
|
case 'bottom' :
|
||
|
case 'top' :
|
||
|
this._buildHorizontal(seriesArray, maxDataLength, locationMap, this.xMarkMap);
|
||
|
break;
|
||
|
case 'left' :
|
||
|
case 'right' :
|
||
|
this._buildVertical(seriesArray, maxDataLength, locationMap, this.xMarkMap);
|
||
|
break;
|
||
|
case 'other' :
|
||
|
this._buildOther(seriesArray, maxDataLength, locationMap, this.xMarkMap);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
for (var i = 0, l = seriesArray.length; i < l; i++) {
|
||
|
this.buildMark(seriesArray[i]);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 数据整形,折线图、柱形图公用方法
|
||
|
* 数组位置映射到系列索引
|
||
|
*/
|
||
|
_mapData: function (seriesArray) {
|
||
|
var series = this.series;
|
||
|
var serie; // 临时映射变量
|
||
|
var dataIndex = 0; // 堆积数据所在位置映射
|
||
|
var stackMap = {}; // 堆积数据位置映射,堆积组在二维中的第几项
|
||
|
var magicStackKey = '__kener__stack__'; // 堆积命名,非堆积数据安单一堆积处理
|
||
|
var stackKey; // 临时映射变量
|
||
|
var serieName; // 临时映射变量
|
||
|
var legend = this.component.legend;
|
||
|
var locationMap = []; // 需要返回的东西:数组位置映射到系列索引
|
||
|
var maxDataLength = 0; // 需要返回的东西:最大数据长度
|
||
|
var iconShape;
|
||
|
// 计算需要显示的个数和分配位置并记在下面这个结构里
|
||
|
for (var i = 0, l = seriesArray.length; i < l; i++) {
|
||
|
serie = series[seriesArray[i]];
|
||
|
serieName = serie.name;
|
||
|
|
||
|
this._sIndex2ShapeMap[seriesArray[i]] = this._sIndex2ShapeMap[seriesArray[i]]
|
||
|
|| this.query(serie,'symbol')
|
||
|
|| this._symbol[i % this._symbol.length];
|
||
|
|
||
|
if (legend){
|
||
|
this.selectedMap[serieName] = legend.isSelected(serieName);
|
||
|
|
||
|
this._sIndex2ColorMap[seriesArray[i]] = legend.getColor(serieName);
|
||
|
|
||
|
iconShape = legend.getItemShape(serieName);
|
||
|
if (iconShape) {
|
||
|
// 回调legend,换一个更形象的icon
|
||
|
var style = iconShape.style;
|
||
|
if (this.type == ecConfig.CHART_TYPE_LINE) {
|
||
|
style.iconType = 'legendLineIcon';
|
||
|
style.symbol = this._sIndex2ShapeMap[seriesArray[i]];
|
||
|
}
|
||
|
else if (serie.itemStyle.normal.barBorderWidth > 0) {
|
||
|
var highlightStyle = iconShape.highlightStyle;
|
||
|
style.brushType = 'both';
|
||
|
style.x += 1;
|
||
|
style.y += 1;
|
||
|
style.width -= 2;
|
||
|
style.height -= 2;
|
||
|
style.strokeColor
|
||
|
= highlightStyle.strokeColor
|
||
|
= serie.itemStyle.normal.barBorderColor;
|
||
|
highlightStyle.lineWidth = 3;
|
||
|
}
|
||
|
|
||
|
legend.setItemShape(serieName, iconShape);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
this.selectedMap[serieName] = true;
|
||
|
this._sIndex2ColorMap[seriesArray[i]] = this.zr.getColor(seriesArray[i]);
|
||
|
}
|
||
|
|
||
|
if (this.selectedMap[serieName]) {
|
||
|
stackKey = serie.stack || (magicStackKey + seriesArray[i]);
|
||
|
if (stackMap[stackKey] == null) {
|
||
|
stackMap[stackKey] = dataIndex;
|
||
|
locationMap[dataIndex] = [seriesArray[i]];
|
||
|
dataIndex++;
|
||
|
}
|
||
|
else {
|
||
|
// 已经分配了位置就推进去就行
|
||
|
locationMap[stackMap[stackKey]].push(seriesArray[i]);
|
||
|
}
|
||
|
}
|
||
|
// 兼职帮算一下最大长度
|
||
|
maxDataLength = Math.max(maxDataLength, serie.data.length);
|
||
|
}
|
||
|
/* 调试输出
|
||
|
var s = '';
|
||
|
for (var i = 0, l = maxDataLength; i < l; i++) {
|
||
|
s = '[';
|
||
|
for (var j = 0, k = locationMap.length; j < k; j++) {
|
||
|
s +='['
|
||
|
for (var m = 0, n = locationMap[j].length - 1; m < n; m++) {
|
||
|
s += series[locationMap[j][m]].data[i] + ','
|
||
|
}
|
||
|
s += series[locationMap[j][locationMap[j].length - 1]]
|
||
|
.data[i];
|
||
|
s += ']'
|
||
|
}
|
||
|
s += ']';
|
||
|
console.log(s);
|
||
|
}
|
||
|
console.log(locationMap)
|
||
|
*/
|
||
|
|
||
|
return {
|
||
|
locationMap: locationMap,
|
||
|
maxDataLength: maxDataLength
|
||
|
};
|
||
|
},
|
||
|
|
||
|
_calculMarkMapXY : function(xMarkMap, locationMap, xy) {
|
||
|
var series = this.series;
|
||
|
for (var j = 0, k = locationMap.length; j < k; j++) {
|
||
|
for (var m = 0, n = locationMap[j].length; m < n; m++) {
|
||
|
var seriesIndex = locationMap[j][m];
|
||
|
var valueIndex = xy == 'xy' ? 0 : '';
|
||
|
var grid = this.component.grid;
|
||
|
var tarMark = xMarkMap[seriesIndex];
|
||
|
|
||
|
if (xy.indexOf('x') != '-1') {
|
||
|
if (tarMark['counter' + valueIndex] > 0) {
|
||
|
tarMark['average' + valueIndex] =
|
||
|
tarMark['sum' + valueIndex] / tarMark['counter' + valueIndex];
|
||
|
}
|
||
|
|
||
|
var x = this.component.xAxis.getAxis(series[seriesIndex].xAxisIndex || 0)
|
||
|
.getCoord(tarMark['average' + valueIndex]);
|
||
|
tarMark['averageLine' + valueIndex] = [
|
||
|
[x, grid.getYend()],
|
||
|
[x, grid.getY()]
|
||
|
];
|
||
|
tarMark['minLine' + valueIndex] = [
|
||
|
[tarMark['minX' + valueIndex], grid.getYend()],
|
||
|
[tarMark['minX' + valueIndex], grid.getY()]
|
||
|
];
|
||
|
tarMark['maxLine' + valueIndex] = [
|
||
|
[tarMark['maxX' + valueIndex], grid.getYend()],
|
||
|
[tarMark['maxX' + valueIndex], grid.getY()]
|
||
|
];
|
||
|
|
||
|
tarMark.isHorizontal = false;
|
||
|
}
|
||
|
|
||
|
valueIndex = xy == 'xy' ? 1 : '';
|
||
|
if (xy.indexOf('y') != '-1') {
|
||
|
if (tarMark['counter' + valueIndex] > 0) {
|
||
|
tarMark['average' + valueIndex] =
|
||
|
tarMark['sum' + valueIndex] / tarMark['counter' + valueIndex];
|
||
|
}
|
||
|
var y = this.component.yAxis.getAxis(series[seriesIndex].yAxisIndex || 0)
|
||
|
.getCoord(tarMark['average' + valueIndex]);
|
||
|
tarMark['averageLine' + valueIndex] = [
|
||
|
[grid.getX(), y],
|
||
|
[grid.getXend(), y]
|
||
|
];
|
||
|
tarMark['minLine' + valueIndex] = [
|
||
|
[grid.getX(), tarMark['minY' + valueIndex]],
|
||
|
[grid.getXend(), tarMark['minY' + valueIndex]]
|
||
|
];
|
||
|
tarMark['maxLine' + valueIndex] = [
|
||
|
[grid.getX(), tarMark['maxY' + valueIndex]],
|
||
|
[grid.getXend(), tarMark['maxY' + valueIndex]]
|
||
|
];
|
||
|
|
||
|
tarMark.isHorizontal = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 添加文本
|
||
|
*/
|
||
|
addLabel: function (tarShape, serie, data, name, orient) {
|
||
|
// 多级控制
|
||
|
var queryTarget = [data, serie];
|
||
|
var nLabel = this.deepMerge(queryTarget, 'itemStyle.normal.label');
|
||
|
var eLabel = this.deepMerge(queryTarget, 'itemStyle.emphasis.label');
|
||
|
|
||
|
var nTextStyle = nLabel.textStyle || {};
|
||
|
var eTextStyle = eLabel.textStyle || {};
|
||
|
|
||
|
if (nLabel.show) {
|
||
|
var style = tarShape.style;
|
||
|
style.text = this._getLabelText(
|
||
|
serie, data, name, 'normal'
|
||
|
);
|
||
|
style.textPosition = nLabel.position == null
|
||
|
? (orient === 'horizontal' ? 'right' : 'top')
|
||
|
: nLabel.position;
|
||
|
style.textColor = nTextStyle.color;
|
||
|
style.textFont = this.getFont(nTextStyle);
|
||
|
style.textAlign = nTextStyle.align;
|
||
|
style.textBaseline = nTextStyle.baseline;
|
||
|
}
|
||
|
if (eLabel.show) {
|
||
|
var highlightStyle = tarShape.highlightStyle;
|
||
|
highlightStyle.text = this._getLabelText(
|
||
|
serie, data, name, 'emphasis'
|
||
|
);
|
||
|
highlightStyle.textPosition = nLabel.show
|
||
|
? tarShape.style.textPosition
|
||
|
: (eLabel.position == null
|
||
|
? (orient === 'horizontal' ? 'right' : 'top')
|
||
|
: eLabel.position);
|
||
|
highlightStyle.textColor = eTextStyle.color;
|
||
|
highlightStyle.textFont = this.getFont(eTextStyle);
|
||
|
highlightStyle.textAlign = eTextStyle.align;
|
||
|
highlightStyle.textBaseline = eTextStyle.baseline;
|
||
|
}
|
||
|
|
||
|
return tarShape;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 根据lable.format计算label text
|
||
|
*/
|
||
|
_getLabelText: function (serie, data, name, status) {
|
||
|
var formatter = this.deepQuery(
|
||
|
[data, serie],
|
||
|
'itemStyle.' + status + '.label.formatter'
|
||
|
);
|
||
|
if (!formatter && status === 'emphasis') {
|
||
|
// emphasis时需要看看normal下是否有formatter
|
||
|
formatter = this.deepQuery(
|
||
|
[data, serie],
|
||
|
'itemStyle.normal.label.formatter'
|
||
|
);
|
||
|
}
|
||
|
|
||
|
var value = this.getDataFromOption(data, '-');
|
||
|
|
||
|
if (formatter) {
|
||
|
if (typeof formatter === 'function') {
|
||
|
return formatter.call(
|
||
|
this.myChart,
|
||
|
{
|
||
|
seriesName: serie.name,
|
||
|
series: serie,
|
||
|
name: name,
|
||
|
value: value,
|
||
|
data: data,
|
||
|
status: status
|
||
|
}
|
||
|
);
|
||
|
}
|
||
|
else if (typeof formatter === 'string') {
|
||
|
formatter = formatter.replace('{a}','{a0}')
|
||
|
.replace('{b}','{b0}')
|
||
|
.replace('{c}','{c0}')
|
||
|
.replace('{a0}', serie.name)
|
||
|
.replace('{b0}', name)
|
||
|
.replace('{c0}', this.numAddCommas(value));
|
||
|
|
||
|
return formatter;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if (value instanceof Array) {
|
||
|
return value[2] != null
|
||
|
? this.numAddCommas(value[2])
|
||
|
: (value[0] + ' , ' + value[1]);
|
||
|
}
|
||
|
else {
|
||
|
return this.numAddCommas(value);
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 标线标注
|
||
|
*/
|
||
|
buildMark: function (seriesIndex) {
|
||
|
var serie = this.series[seriesIndex];
|
||
|
if (this.selectedMap[serie.name]) {
|
||
|
serie.markLine && this._buildMarkLine(seriesIndex);
|
||
|
serie.markPoint && this._buildMarkPoint(seriesIndex);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 标注逻辑
|
||
|
*/
|
||
|
_buildMarkPoint: function (seriesIndex) {
|
||
|
var attachStyle = (this.markAttachStyle || {})[seriesIndex];
|
||
|
var serie = this.series[seriesIndex];
|
||
|
var mpData;
|
||
|
var pos;
|
||
|
var markPoint = zrUtil.clone(serie.markPoint);
|
||
|
for (var i = 0, l = markPoint.data.length; i < l; i++) {
|
||
|
mpData = markPoint.data[i];
|
||
|
pos = this.getMarkCoord(seriesIndex, mpData);
|
||
|
mpData.x = mpData.x != null ? mpData.x : pos[0];
|
||
|
mpData.y = mpData.y != null ? mpData.y : pos[1];
|
||
|
if (mpData.type
|
||
|
&& (mpData.type === 'max' || mpData.type === 'min')
|
||
|
) {
|
||
|
// 特殊值内置支持
|
||
|
mpData.value = pos[3];
|
||
|
mpData.name = mpData.name || mpData.type;
|
||
|
mpData.symbolSize = mpData.symbolSize
|
||
|
|| (zrArea.getTextWidth(pos[3], this.getFont()) / 2 + 5);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var shapeList = this._markPoint(seriesIndex, markPoint);
|
||
|
|
||
|
for (var i = 0, l = shapeList.length; i < l; i++) {
|
||
|
var tarShape = shapeList[i];
|
||
|
tarShape.zlevel = this.getZlevelBase();
|
||
|
tarShape.z = this.getZBase() + 1;
|
||
|
for (var key in attachStyle) {
|
||
|
tarShape[key] = zrUtil.clone(attachStyle[key]);
|
||
|
}
|
||
|
this.shapeList.push(tarShape);
|
||
|
}
|
||
|
// 个别特殊图表需要自己addShape
|
||
|
if (this.type === ecConfig.CHART_TYPE_FORCE
|
||
|
|| this.type === ecConfig.CHART_TYPE_CHORD
|
||
|
) {
|
||
|
for (var i = 0, l = shapeList.length; i < l; i++) {
|
||
|
this.zr.addShape(shapeList[i]);
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 标线逻辑
|
||
|
*/
|
||
|
_buildMarkLine: function (seriesIndex) {
|
||
|
var attachStyle = (this.markAttachStyle || {})[seriesIndex];
|
||
|
var serie = this.series[seriesIndex];
|
||
|
var pos;
|
||
|
var markLine = zrUtil.clone(serie.markLine);
|
||
|
for (var i = 0, l = markLine.data.length; i < l; i++) {
|
||
|
var mlData = markLine.data[i];
|
||
|
if (mlData.type
|
||
|
&& (mlData.type === 'max' || mlData.type === 'min' || mlData.type === 'average')
|
||
|
) {
|
||
|
// 特殊值内置支持
|
||
|
pos = this.getMarkCoord(seriesIndex, mlData);
|
||
|
markLine.data[i] = [zrUtil.clone(mlData), {}];
|
||
|
markLine.data[i][0].name = mlData.name || mlData.type;
|
||
|
markLine.data[i][0].value = mlData.type !== 'average'
|
||
|
? pos[3]
|
||
|
: +pos[3].toFixed(
|
||
|
markLine.precision != null
|
||
|
? markLine.precision
|
||
|
: this.deepQuery(
|
||
|
[this.ecTheme, ecConfig],
|
||
|
'markLine.precision'
|
||
|
)
|
||
|
);
|
||
|
pos = pos[2];
|
||
|
mlData = [{},{}];
|
||
|
}
|
||
|
else {
|
||
|
pos = [
|
||
|
this.getMarkCoord(seriesIndex, mlData[0]),
|
||
|
this.getMarkCoord(seriesIndex, mlData[1])
|
||
|
];
|
||
|
}
|
||
|
if (pos == null || pos[0] == null || pos[1] == null) {
|
||
|
// 不在显示区域内
|
||
|
continue;
|
||
|
}
|
||
|
markLine.data[i][0].x = mlData[0].x != null ? mlData[0].x : pos[0][0];
|
||
|
markLine.data[i][0].y = mlData[0].y != null ? mlData[0].y : pos[0][1];
|
||
|
markLine.data[i][1].x = mlData[1].x != null ? mlData[1].x : pos[1][0];
|
||
|
markLine.data[i][1].y = mlData[1].y != null ? mlData[1].y : pos[1][1];
|
||
|
}
|
||
|
|
||
|
var shapeList = this._markLine(seriesIndex, markLine);
|
||
|
|
||
|
var isLarge = markLine.large;
|
||
|
|
||
|
if (isLarge) {
|
||
|
var shapeBundle = new ShapeBundle({
|
||
|
style: {
|
||
|
shapeList: shapeList
|
||
|
}
|
||
|
});
|
||
|
var firstShape = shapeList[0];
|
||
|
if (firstShape) {
|
||
|
zrUtil.merge(shapeBundle.style, firstShape.style);
|
||
|
zrUtil.merge(shapeBundle.highlightStyle = {}, firstShape.highlightStyle);
|
||
|
shapeBundle.style.brushType = 'stroke';
|
||
|
shapeBundle.zlevel = this.getZlevelBase();
|
||
|
shapeBundle.z = this.getZBase() + 1;
|
||
|
shapeBundle.hoverable = false;
|
||
|
for (var key in attachStyle) {
|
||
|
shapeBundle[key] = zrUtil.clone(attachStyle[key]);
|
||
|
}
|
||
|
}
|
||
|
this.shapeList.push(shapeBundle);
|
||
|
this.zr.addShape(shapeBundle);
|
||
|
|
||
|
shapeBundle._mark = 'largeLine';
|
||
|
var effect = markLine.effect;
|
||
|
if (effect.show) {
|
||
|
shapeBundle.effect = effect;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
for (var i = 0, l = shapeList.length; i < l; i++) {
|
||
|
var tarShape = shapeList[i];
|
||
|
tarShape.zlevel = this.getZlevelBase();
|
||
|
tarShape.z = this.getZBase() + 1;
|
||
|
for (var key in attachStyle) {
|
||
|
tarShape[key] = zrUtil.clone(attachStyle[key]);
|
||
|
}
|
||
|
this.shapeList.push(tarShape);
|
||
|
}
|
||
|
// 个别特殊图表需要自己addShape
|
||
|
if (this.type === ecConfig.CHART_TYPE_FORCE
|
||
|
|| this.type === ecConfig.CHART_TYPE_CHORD
|
||
|
) {
|
||
|
for (var i = 0, l = shapeList.length; i < l; i++) {
|
||
|
this.zr.addShape(shapeList[i]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 标注多级控制构造
|
||
|
*/
|
||
|
_markPoint: function (seriesIndex, mpOption) {
|
||
|
var serie = this.series[seriesIndex];
|
||
|
var component = this.component;
|
||
|
zrUtil.merge(
|
||
|
zrUtil.merge(
|
||
|
mpOption,
|
||
|
zrUtil.clone(this.ecTheme.markPoint || {})
|
||
|
),
|
||
|
zrUtil.clone(ecConfig.markPoint)
|
||
|
);
|
||
|
|
||
|
mpOption.name = serie.name;
|
||
|
|
||
|
var pList = [];
|
||
|
var data = mpOption.data;
|
||
|
var itemShape;
|
||
|
|
||
|
var dataRange = component.dataRange;
|
||
|
var legend = component.legend;
|
||
|
var color;
|
||
|
var value;
|
||
|
var queryTarget;
|
||
|
var nColor;
|
||
|
var eColor;
|
||
|
var effect;
|
||
|
var zrWidth = this.zr.getWidth();
|
||
|
var zrHeight = this.zr.getHeight();
|
||
|
|
||
|
if (!mpOption.large) {
|
||
|
for (var i = 0, l = data.length; i < l; i++) {
|
||
|
if (data[i].x == null || data[i].y == null) {
|
||
|
continue;
|
||
|
}
|
||
|
value = data[i].value != null ? data[i].value : '';
|
||
|
// 图例
|
||
|
if (legend) {
|
||
|
color = legend.getColor(serie.name);
|
||
|
}
|
||
|
// 值域
|
||
|
if (dataRange) {
|
||
|
color = isNaN(value) ? color : dataRange.getColor(value);
|
||
|
|
||
|
queryTarget = [data[i], mpOption];
|
||
|
nColor = this.deepQuery(queryTarget, 'itemStyle.normal.color')
|
||
|
|| color;
|
||
|
eColor = this.deepQuery(queryTarget, 'itemStyle.emphasis.color')
|
||
|
|| nColor;
|
||
|
// 有值域,并且值域返回null且用户没有自己定义颜色,则隐藏这个mark
|
||
|
if (nColor == null && eColor == null) {
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
color = color == null ? this.zr.getColor(seriesIndex) : color;
|
||
|
|
||
|
// 标准化一些参数
|
||
|
data[i].tooltip = data[i].tooltip
|
||
|
|| mpOption.tooltip
|
||
|
|| {trigger:'item'}; // tooltip.trigger指定为item
|
||
|
data[i].name = data[i].name != null ? data[i].name : '';
|
||
|
data[i].value = value;
|
||
|
|
||
|
// 复用getSymbolShape
|
||
|
itemShape = this.getSymbolShape(
|
||
|
mpOption, seriesIndex, // 系列
|
||
|
data[i], i, data[i].name, // 数据
|
||
|
this.parsePercent(data[i].x, zrWidth), // 坐标
|
||
|
this.parsePercent(data[i].y, zrHeight), // 坐标
|
||
|
'pin', color, // 默认symbol和color
|
||
|
'rgba(0,0,0,0)',
|
||
|
'horizontal' // 走向,用于默认文字定位
|
||
|
);
|
||
|
itemShape._mark = 'point';
|
||
|
|
||
|
effect = this.deepMerge(
|
||
|
[data[i], mpOption],
|
||
|
'effect'
|
||
|
);
|
||
|
if (effect.show) {
|
||
|
itemShape.effect = effect;
|
||
|
}
|
||
|
|
||
|
if (serie.type === ecConfig.CHART_TYPE_MAP) {
|
||
|
itemShape._geo = this.getMarkGeo(data[i]);
|
||
|
}
|
||
|
|
||
|
// 重新pack一下数据
|
||
|
ecData.pack(
|
||
|
itemShape,
|
||
|
serie, seriesIndex,
|
||
|
data[i], i,
|
||
|
data[i].name,
|
||
|
value
|
||
|
);
|
||
|
pList.push(itemShape);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
// 大规模MarkPoint
|
||
|
itemShape = this.getLargeMarkPointShape(seriesIndex, mpOption);
|
||
|
itemShape._mark = 'largePoint';
|
||
|
itemShape && pList.push(itemShape);
|
||
|
}
|
||
|
return pList;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 标线多级控制构造
|
||
|
*/
|
||
|
_markLine: (function () {
|
||
|
function normalizeOptionValue(mlOption, key) {
|
||
|
mlOption[key] = mlOption[key] instanceof Array
|
||
|
? mlOption[key].length > 1
|
||
|
? mlOption[key]
|
||
|
: [mlOption[key][0], mlOption[key][0]]
|
||
|
: [mlOption[key], mlOption[key]];
|
||
|
}
|
||
|
|
||
|
return function (seriesIndex, mlOption) {
|
||
|
var serie = this.series[seriesIndex];
|
||
|
var component = this.component;
|
||
|
var dataRange = component.dataRange;
|
||
|
var legend = component.legend;
|
||
|
|
||
|
zrUtil.merge(
|
||
|
zrUtil.merge(
|
||
|
mlOption,
|
||
|
zrUtil.clone(this.ecTheme.markLine || {})
|
||
|
),
|
||
|
zrUtil.clone(ecConfig.markLine)
|
||
|
);
|
||
|
|
||
|
var defaultColor = legend ? legend.getColor(serie.name)
|
||
|
: this.zr.getColor(seriesIndex);
|
||
|
|
||
|
// 标准化一些同时支持Array和String的参数
|
||
|
normalizeOptionValue(mlOption, 'symbol');
|
||
|
normalizeOptionValue(mlOption, 'symbolSize');
|
||
|
normalizeOptionValue(mlOption, 'symbolRotate');
|
||
|
|
||
|
// Normalize and filter data
|
||
|
var data = mlOption.data;
|
||
|
var edges = [];
|
||
|
var zrWidth = this.zr.getWidth();
|
||
|
var zrHeight = this.zr.getHeight();
|
||
|
for (var i = 0; i < data.length; i++) {
|
||
|
var mlData = data[i];
|
||
|
if (isCoordAvailable(mlData[0])
|
||
|
&& isCoordAvailable(mlData[1])
|
||
|
) {
|
||
|
// 组装一个mergeData
|
||
|
var mergeData = this.deepMerge(mlData);
|
||
|
var queryTarget = [mergeData, mlOption];
|
||
|
var color = defaultColor;
|
||
|
var value = mergeData.value != null ? mergeData.value : '';
|
||
|
// 值域
|
||
|
if (dataRange) {
|
||
|
color = isNaN(value) ? color : dataRange.getColor(value);
|
||
|
|
||
|
var nColor = this.deepQuery(queryTarget, 'itemStyle.normal.color')
|
||
|
|| color;
|
||
|
var eColor = this.deepQuery(queryTarget, 'itemStyle.emphasis.color')
|
||
|
|| nColor;
|
||
|
// 有值域,并且值域返回null且用户没有自己定义颜色,则隐藏这个mark
|
||
|
if (nColor == null && eColor == null) {
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
// 标准化一些参数
|
||
|
mlData[0].tooltip = mergeData.tooltip
|
||
|
|| mlOption.tooltip
|
||
|
|| {trigger:'item'}; // tooltip.trigger指定为item
|
||
|
mlData[0].name = mlData[0].name || '';
|
||
|
mlData[1].name = mlData[1].name || '';
|
||
|
mlData[0].value = value;
|
||
|
|
||
|
edges.push({
|
||
|
points: [
|
||
|
[this.parsePercent(mlData[0].x, zrWidth),
|
||
|
this.parsePercent(mlData[0].y, zrHeight)],
|
||
|
[this.parsePercent(mlData[1].x, zrWidth),
|
||
|
this.parsePercent(mlData[1].y, zrHeight)]
|
||
|
],
|
||
|
rawData: mlData,
|
||
|
color: color
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var enableBundling = this.query(mlOption, 'bundling.enable');
|
||
|
if (enableBundling) {
|
||
|
var edgeBundling = new EdgeBundling();
|
||
|
edgeBundling.maxTurningAngle = this.query(
|
||
|
mlOption, 'bundling.maxTurningAngle'
|
||
|
) / 180 * Math.PI;
|
||
|
edges = edgeBundling.run(edges);
|
||
|
}
|
||
|
|
||
|
mlOption.name = serie.name;
|
||
|
|
||
|
var shapeList = [];
|
||
|
|
||
|
for (var i = 0, l = edges.length; i < l; i++) {
|
||
|
var edge = edges[i];
|
||
|
var rawEdge = edge.rawEdge || edge;
|
||
|
var mlData = rawEdge.rawData;
|
||
|
var value = mlData.value != null ? mlData.value : '';
|
||
|
|
||
|
var itemShape = this.getMarkLineShape(
|
||
|
mlOption,
|
||
|
seriesIndex,
|
||
|
mlData,
|
||
|
i,
|
||
|
edge.points,
|
||
|
enableBundling,
|
||
|
rawEdge.color
|
||
|
);
|
||
|
itemShape._mark = 'line';
|
||
|
|
||
|
var effect = this.deepMerge(
|
||
|
[mlData[0], mlData[1], mlOption],
|
||
|
'effect'
|
||
|
);
|
||
|
if (effect.show) {
|
||
|
itemShape.effect = effect;
|
||
|
itemShape.effect.large = mlOption.large;
|
||
|
}
|
||
|
|
||
|
if (serie.type === ecConfig.CHART_TYPE_MAP) {
|
||
|
itemShape._geo = [
|
||
|
this.getMarkGeo(mlData[0]),
|
||
|
this.getMarkGeo(mlData[1])
|
||
|
];
|
||
|
}
|
||
|
|
||
|
// 重新pack一下数据
|
||
|
ecData.pack(
|
||
|
itemShape,
|
||
|
serie, seriesIndex,
|
||
|
mlData[0], i,
|
||
|
mlData[0].name
|
||
|
// 不要帮我代码规范
|
||
|
+ (mlData[1].name !== '' ? (' > ' + mlData[1].name) : ''),
|
||
|
value
|
||
|
);
|
||
|
shapeList.push(itemShape);
|
||
|
}
|
||
|
|
||
|
return shapeList;
|
||
|
};
|
||
|
})(),
|
||
|
|
||
|
getMarkCoord: function () {
|
||
|
// 无转换位置
|
||
|
return [0, 0];
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* symbol构造器
|
||
|
*/
|
||
|
getSymbolShape: function (
|
||
|
serie, seriesIndex, // 系列
|
||
|
data, dataIndex, name, // 数据
|
||
|
x, y, // 坐标
|
||
|
symbol, color, // 默认symbol和color,来自legend或dataRange全局分配
|
||
|
emptyColor, // 折线的emptySymbol用白色填充
|
||
|
orient // 走向,用于默认文字定位
|
||
|
) {
|
||
|
var queryTarget = [data, serie];
|
||
|
var value = this.getDataFromOption(data, '-');
|
||
|
|
||
|
symbol = this.deepQuery(queryTarget, 'symbol') || symbol;
|
||
|
var symbolSize = this.deepQuery(queryTarget, 'symbolSize');
|
||
|
symbolSize = typeof symbolSize === 'function'
|
||
|
? symbolSize(value)
|
||
|
: symbolSize;
|
||
|
if (typeof symbolSize === 'number') {
|
||
|
symbolSize = [symbolSize, symbolSize];
|
||
|
}
|
||
|
var symbolRotate = this.deepQuery(queryTarget, 'symbolRotate');
|
||
|
|
||
|
var normal = this.deepMerge(
|
||
|
queryTarget,
|
||
|
'itemStyle.normal'
|
||
|
);
|
||
|
var emphasis = this.deepMerge(
|
||
|
queryTarget,
|
||
|
'itemStyle.emphasis'
|
||
|
);
|
||
|
var nBorderWidth = normal.borderWidth != null
|
||
|
? normal.borderWidth
|
||
|
: (normal.lineStyle && normal.lineStyle.width);
|
||
|
if (nBorderWidth == null) {
|
||
|
nBorderWidth = symbol.match('empty') ? 2 : 0;
|
||
|
}
|
||
|
var eBorderWidth = emphasis.borderWidth != null
|
||
|
? emphasis.borderWidth
|
||
|
: (emphasis.lineStyle && emphasis.lineStyle.width);
|
||
|
if (eBorderWidth == null) {
|
||
|
eBorderWidth = nBorderWidth + 2;
|
||
|
}
|
||
|
|
||
|
var nColor = this.getItemStyleColor(normal.color, seriesIndex, dataIndex, data);
|
||
|
var eColor = this.getItemStyleColor(emphasis.color, seriesIndex, dataIndex, data);
|
||
|
|
||
|
var width = symbolSize[0];
|
||
|
var height = symbolSize[1];
|
||
|
var itemShape = new IconShape({
|
||
|
style: {
|
||
|
iconType: symbol.replace('empty', '').toLowerCase(),
|
||
|
x: x - width,
|
||
|
y: y - height,
|
||
|
width: width * 2,
|
||
|
height: height * 2,
|
||
|
brushType: 'both',
|
||
|
color: symbol.match('empty')
|
||
|
? emptyColor
|
||
|
: (nColor || color),
|
||
|
strokeColor: normal.borderColor || nColor || color,
|
||
|
lineWidth: nBorderWidth
|
||
|
},
|
||
|
highlightStyle: {
|
||
|
color: symbol.match('empty')
|
||
|
? emptyColor
|
||
|
: (eColor || nColor || color),
|
||
|
strokeColor: emphasis.borderColor
|
||
|
|| normal.borderColor
|
||
|
|| eColor
|
||
|
|| nColor
|
||
|
|| color,
|
||
|
lineWidth: eBorderWidth
|
||
|
},
|
||
|
clickable: this.deepQuery(queryTarget, 'clickable')
|
||
|
});
|
||
|
|
||
|
if (symbol.match('image')) {
|
||
|
itemShape.style.image = symbol.replace(new RegExp('^image:\\/\\/'), '');
|
||
|
itemShape = new ImageShape({
|
||
|
style: itemShape.style,
|
||
|
highlightStyle: itemShape.highlightStyle,
|
||
|
clickable: this.deepQuery(queryTarget, 'clickable')
|
||
|
});
|
||
|
}
|
||
|
|
||
|
if (symbolRotate != null) {
|
||
|
itemShape.rotation = [
|
||
|
symbolRotate * Math.PI / 180, x, y
|
||
|
];
|
||
|
}
|
||
|
|
||
|
if (symbol.match('star')) {
|
||
|
itemShape.style.iconType = 'star';
|
||
|
itemShape.style.n =
|
||
|
(symbol.replace('empty', '').replace('star','') - 0) || 5;
|
||
|
}
|
||
|
|
||
|
if (symbol === 'none') {
|
||
|
itemShape.invisible = true;
|
||
|
itemShape.hoverable = false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
if (this.deepQuery([data, serie, option], 'calculable')) {
|
||
|
this.setCalculable(itemShape);
|
||
|
itemShape.draggable = true;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
itemShape = this.addLabel(
|
||
|
itemShape,
|
||
|
serie, data, name,
|
||
|
orient
|
||
|
);
|
||
|
|
||
|
if (symbol.match('empty')) {
|
||
|
if (itemShape.style.textColor == null) {
|
||
|
itemShape.style.textColor = itemShape.style.strokeColor;
|
||
|
}
|
||
|
if (itemShape.highlightStyle.textColor == null) {
|
||
|
itemShape.highlightStyle.textColor =
|
||
|
itemShape.highlightStyle.strokeColor;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ecData.pack(
|
||
|
itemShape,
|
||
|
serie, seriesIndex,
|
||
|
data, dataIndex,
|
||
|
name
|
||
|
);
|
||
|
|
||
|
itemShape._x = x;
|
||
|
itemShape._y = y;
|
||
|
|
||
|
itemShape._dataIndex = dataIndex;
|
||
|
itemShape._seriesIndex = seriesIndex;
|
||
|
|
||
|
return itemShape;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 标线构造器
|
||
|
*/
|
||
|
getMarkLineShape: function (
|
||
|
mlOption, // 系列
|
||
|
seriesIndex, // 系列索引
|
||
|
data, // 数据
|
||
|
dataIndex, // 数据索引
|
||
|
points, // 坐标点
|
||
|
bundling, // 是否边捆绑过
|
||
|
color // 默认color,来自legend或dataRange全局分配
|
||
|
) {
|
||
|
var value0 = data[0].value != null ? data[0].value : '-';
|
||
|
var value1 = data[1].value != null ? data[1].value : '-';
|
||
|
var symbol = [
|
||
|
data[0].symbol || mlOption.symbol[0],
|
||
|
data[1].symbol || mlOption.symbol[1]
|
||
|
];
|
||
|
var symbolSize = [
|
||
|
data[0].symbolSize || mlOption.symbolSize[0],
|
||
|
data[1].symbolSize || mlOption.symbolSize[1]
|
||
|
];
|
||
|
symbolSize[0] = typeof symbolSize[0] === 'function'
|
||
|
? symbolSize[0](value0)
|
||
|
: symbolSize[0];
|
||
|
symbolSize[1] = typeof symbolSize[1] === 'function'
|
||
|
? symbolSize[1](value1)
|
||
|
: symbolSize[1];
|
||
|
var symbolRotate = [
|
||
|
this.query(data[0], 'symbolRotate') || mlOption.symbolRotate[0],
|
||
|
this.query(data[1], 'symbolRotate') || mlOption.symbolRotate[1]
|
||
|
];
|
||
|
//console.log(symbol, symbolSize, symbolRotate);
|
||
|
|
||
|
var queryTarget = [data[0], data[1], mlOption];
|
||
|
var normal = this.deepMerge(
|
||
|
queryTarget,
|
||
|
'itemStyle.normal'
|
||
|
);
|
||
|
normal.color = this.getItemStyleColor(normal.color, seriesIndex, dataIndex, data);
|
||
|
var emphasis = this.deepMerge(
|
||
|
queryTarget,
|
||
|
'itemStyle.emphasis'
|
||
|
);
|
||
|
emphasis.color = this.getItemStyleColor(emphasis.color, seriesIndex, dataIndex, data);
|
||
|
|
||
|
var nlineStyle = normal.lineStyle;
|
||
|
var elineStyle = emphasis.lineStyle;
|
||
|
|
||
|
var nBorderWidth = nlineStyle.width;
|
||
|
if (nBorderWidth == null) {
|
||
|
nBorderWidth = normal.borderWidth;
|
||
|
}
|
||
|
var eBorderWidth = elineStyle.width;
|
||
|
if (eBorderWidth == null) {
|
||
|
eBorderWidth = emphasis.borderWidth != null
|
||
|
? emphasis.borderWidth
|
||
|
: (nBorderWidth + 2);
|
||
|
}
|
||
|
var smoothness = this.deepQuery(queryTarget, 'smoothness');
|
||
|
if (! this.deepQuery(queryTarget, 'smooth')) {
|
||
|
smoothness = 0;
|
||
|
}
|
||
|
|
||
|
var ShapeCtor = bundling ? PolylineShape : MarkLineShape;
|
||
|
var itemShape = new ShapeCtor({
|
||
|
style: {
|
||
|
symbol: symbol,
|
||
|
symbolSize: symbolSize,
|
||
|
symbolRotate: symbolRotate,
|
||
|
// data: [data[0].name,data[1].name],
|
||
|
brushType: 'both',
|
||
|
lineType: nlineStyle.type,
|
||
|
shadowColor: nlineStyle.shadowColor
|
||
|
|| nlineStyle.color
|
||
|
|| normal.borderColor
|
||
|
|| normal.color
|
||
|
|| color,
|
||
|
shadowBlur: nlineStyle.shadowBlur,
|
||
|
shadowOffsetX: nlineStyle.shadowOffsetX,
|
||
|
shadowOffsetY: nlineStyle.shadowOffsetY,
|
||
|
color: normal.color || color,
|
||
|
strokeColor: nlineStyle.color
|
||
|
|| normal.borderColor
|
||
|
|| normal.color
|
||
|
|| color,
|
||
|
lineWidth: nBorderWidth,
|
||
|
symbolBorderColor: normal.borderColor
|
||
|
|| normal.color
|
||
|
|| color,
|
||
|
symbolBorder: normal.borderWidth
|
||
|
},
|
||
|
highlightStyle: {
|
||
|
shadowColor: elineStyle.shadowColor,
|
||
|
shadowBlur: elineStyle.shadowBlur,
|
||
|
shadowOffsetX: elineStyle.shadowOffsetX,
|
||
|
shadowOffsetY: elineStyle.shadowOffsetY,
|
||
|
color: emphasis.color|| normal.color || color,
|
||
|
strokeColor: elineStyle.color
|
||
|
|| nlineStyle.color
|
||
|
|| emphasis.borderColor
|
||
|
|| normal.borderColor
|
||
|
|| emphasis.color
|
||
|
|| normal.color
|
||
|
|| color,
|
||
|
lineWidth: eBorderWidth,
|
||
|
symbolBorderColor: emphasis.borderColor
|
||
|
|| normal.borderColor
|
||
|
|| emphasis.color
|
||
|
|| normal.color
|
||
|
|| color,
|
||
|
symbolBorder: emphasis.borderWidth == null
|
||
|
? (normal.borderWidth + 2)
|
||
|
: (emphasis.borderWidth)
|
||
|
},
|
||
|
clickable: this.deepQuery(queryTarget, 'clickable')
|
||
|
});
|
||
|
var shapeStyle = itemShape.style;
|
||
|
if (bundling) {
|
||
|
shapeStyle.pointList = points;
|
||
|
shapeStyle.smooth = smoothness;
|
||
|
}
|
||
|
else {
|
||
|
shapeStyle.xStart = points[0][0];
|
||
|
shapeStyle.yStart = points[0][1];
|
||
|
shapeStyle.xEnd = points[1][0];
|
||
|
shapeStyle.yEnd = points[1][1];
|
||
|
shapeStyle.curveness = smoothness;
|
||
|
itemShape.updatePoints(itemShape.style);
|
||
|
}
|
||
|
|
||
|
itemShape = this.addLabel(
|
||
|
itemShape,
|
||
|
mlOption,
|
||
|
data[0],
|
||
|
data[0].name + ' : ' + data[1].name
|
||
|
);
|
||
|
|
||
|
return itemShape;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 大规模标注构造器
|
||
|
*/
|
||
|
getLargeMarkPointShape: function(seriesIndex, mpOption) {
|
||
|
var serie = this.series[seriesIndex];
|
||
|
var component = this.component;
|
||
|
var data = mpOption.data;
|
||
|
var itemShape;
|
||
|
|
||
|
var dataRange = component.dataRange;
|
||
|
var legend = component.legend;
|
||
|
var color;
|
||
|
var value;
|
||
|
var queryTarget = [data[0], mpOption];
|
||
|
var nColor;
|
||
|
var eColor;
|
||
|
var effect;
|
||
|
|
||
|
// 图例
|
||
|
if (legend) {
|
||
|
color = legend.getColor(serie.name);
|
||
|
}
|
||
|
// 值域
|
||
|
if (dataRange) {
|
||
|
value = data[0].value != null ? data[0].value : '';
|
||
|
color = isNaN(value) ? color : dataRange.getColor(value);
|
||
|
|
||
|
nColor = this.deepQuery(queryTarget, 'itemStyle.normal.color')
|
||
|
|| color;
|
||
|
eColor = this.deepQuery(queryTarget, 'itemStyle.emphasis.color')
|
||
|
|| nColor;
|
||
|
// 有值域,并且值域返回null且用户没有自己定义颜色,则隐藏这个mark
|
||
|
if (nColor == null && eColor == null) {
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
color = this.deepMerge(queryTarget, 'itemStyle.normal').color
|
||
|
|| color;
|
||
|
|
||
|
var symbol = this.deepQuery(queryTarget, 'symbol') || 'circle';
|
||
|
symbol = symbol.replace('empty', '').replace(/\d/g, '');
|
||
|
|
||
|
effect = this.deepMerge(
|
||
|
[data[0], mpOption],
|
||
|
'effect'
|
||
|
);
|
||
|
|
||
|
var devicePixelRatio = window.devicePixelRatio || 1;
|
||
|
|
||
|
//console.log(data)
|
||
|
itemShape = new SymbolShape({
|
||
|
style: {
|
||
|
pointList: data,
|
||
|
color: color,
|
||
|
strokeColor: color,
|
||
|
shadowColor: effect.shadowColor || color,
|
||
|
shadowBlur: (effect.shadowBlur != null ? effect.shadowBlur : 8)
|
||
|
* devicePixelRatio,
|
||
|
size: this.deepQuery(queryTarget, 'symbolSize'),
|
||
|
iconType: symbol,
|
||
|
brushType: 'fill',
|
||
|
lineWidth:1
|
||
|
},
|
||
|
draggable: false,
|
||
|
hoverable: false
|
||
|
});
|
||
|
|
||
|
if (effect.show) {
|
||
|
itemShape.effect = effect;
|
||
|
}
|
||
|
|
||
|
return itemShape;
|
||
|
},
|
||
|
|
||
|
backupShapeList: function () {
|
||
|
if (this.shapeList && this.shapeList.length > 0) {
|
||
|
this.lastShapeList = this.shapeList;
|
||
|
this.shapeList = [];
|
||
|
}
|
||
|
else {
|
||
|
this.lastShapeList = [];
|
||
|
}
|
||
|
},
|
||
|
|
||
|
addShapeList: function () {
|
||
|
var maxLenth = this.option.animationThreshold / (this.canvasSupported ? 2 : 4);
|
||
|
var lastShapeList = this.lastShapeList;
|
||
|
var shapeList = this.shapeList;
|
||
|
var isUpdate = lastShapeList.length > 0;
|
||
|
var duration = isUpdate
|
||
|
? this.query(this.option, 'animationDurationUpdate')
|
||
|
: this.query(this.option, 'animationDuration');
|
||
|
var easing = this.query(this.option, 'animationEasing');
|
||
|
var delay;
|
||
|
var key;
|
||
|
var oldMap = {};
|
||
|
var newMap = {};
|
||
|
if (this.option.animation
|
||
|
&& !this.option.renderAsImage
|
||
|
&& shapeList.length < maxLenth
|
||
|
&& !this.motionlessOnce
|
||
|
) {
|
||
|
// 通过已有的shape做动画过渡
|
||
|
for (var i = 0, l = lastShapeList.length; i < l; i++) {
|
||
|
key = this._getAnimationKey(lastShapeList[i]);
|
||
|
if (key.match('undefined')) {
|
||
|
this.zr.delShape(lastShapeList[i].id); // 非关键元素直接删除
|
||
|
}
|
||
|
else {
|
||
|
key += lastShapeList[i].type;
|
||
|
// https://github.com/ecomfe/echarts/issues/1219#issuecomment-71987602
|
||
|
// 响应中断可能产生的重复元素
|
||
|
if (oldMap[key]) {
|
||
|
this.zr.delShape(lastShapeList[i].id);
|
||
|
}
|
||
|
else {
|
||
|
oldMap[key] = lastShapeList[i];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
for (var i = 0, l = shapeList.length; i < l; i++) {
|
||
|
key = this._getAnimationKey(shapeList[i]);
|
||
|
if (key.match('undefined')) {
|
||
|
this.zr.addShape(shapeList[i]); // 非关键元素直接添加
|
||
|
}
|
||
|
else {
|
||
|
key += shapeList[i].type;
|
||
|
newMap[key] = shapeList[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (key in oldMap) {
|
||
|
if (!newMap[key]) {
|
||
|
// 新的没有 删除
|
||
|
this.zr.delShape(oldMap[key].id);
|
||
|
}
|
||
|
}
|
||
|
for (key in newMap) {
|
||
|
if (oldMap[key]) {
|
||
|
// 新旧都有 动画过渡
|
||
|
this.zr.delShape(oldMap[key].id);
|
||
|
this._animateMod(
|
||
|
oldMap[key], newMap[key], duration, easing, 0, isUpdate
|
||
|
);
|
||
|
}
|
||
|
else {
|
||
|
// 新有旧没有 添加并动画过渡
|
||
|
//this._animateAdd(newMap[key], duration, easing);
|
||
|
delay = (this.type == ecConfig.CHART_TYPE_LINE
|
||
|
|| this.type == ecConfig.CHART_TYPE_RADAR)
|
||
|
&& key.indexOf('icon') !== 0
|
||
|
? duration / 2
|
||
|
: 0;
|
||
|
this._animateMod(
|
||
|
false, newMap[key], duration, easing, delay, isUpdate
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
this.zr.refresh();
|
||
|
this.animationEffect();
|
||
|
}
|
||
|
else {
|
||
|
this.motionlessOnce = false;
|
||
|
// clear old
|
||
|
this.zr.delShape(lastShapeList);
|
||
|
// 直接添加
|
||
|
for (var i = 0, l = shapeList.length; i < l; i++) {
|
||
|
this.zr.addShape(shapeList[i]);
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_getAnimationKey: function(shape) {
|
||
|
if (this.type != ecConfig.CHART_TYPE_MAP
|
||
|
&& this.type != ecConfig.CHART_TYPE_TREEMAP
|
||
|
&& this.type != ecConfig.CHART_TYPE_VENN
|
||
|
) {
|
||
|
return ecData.get(shape, 'seriesIndex') + '_'
|
||
|
+ ecData.get(shape, 'dataIndex')
|
||
|
+ (shape._mark ? shape._mark : '')
|
||
|
+ (this.type === ecConfig.CHART_TYPE_RADAR
|
||
|
? ecData.get(shape, 'special') : '');
|
||
|
}
|
||
|
else {
|
||
|
return ecData.get(shape, 'seriesIndex') + '_'
|
||
|
+ ecData.get(shape, 'dataIndex')
|
||
|
+ (shape._mark ? shape._mark : 'undefined');
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 动画过渡
|
||
|
*/
|
||
|
_animateMod: function (oldShape, newShape, duration, easing, delay, isUpdate) {
|
||
|
switch (newShape.type) {
|
||
|
case 'polyline' :
|
||
|
case 'half-smooth-polygon' :
|
||
|
ecAnimation.pointList(this.zr, oldShape, newShape, duration, easing);
|
||
|
break;
|
||
|
case 'rectangle' :
|
||
|
ecAnimation.rectangle(this.zr, oldShape, newShape, duration, easing);
|
||
|
break;
|
||
|
case 'image' :
|
||
|
case 'icon' :
|
||
|
ecAnimation.icon(this.zr, oldShape, newShape, duration, easing, delay);
|
||
|
break;
|
||
|
case 'candle' :
|
||
|
if (!isUpdate) {
|
||
|
ecAnimation.candle(this.zr, oldShape, newShape, duration, easing);
|
||
|
}
|
||
|
else {
|
||
|
this.zr.addShape(newShape);
|
||
|
}
|
||
|
break;
|
||
|
case 'ring' :
|
||
|
case 'sector' :
|
||
|
case 'circle' :
|
||
|
if (!isUpdate) {
|
||
|
// 进入动画,加旋转
|
||
|
ecAnimation.ring(
|
||
|
this.zr,
|
||
|
oldShape,
|
||
|
newShape,
|
||
|
duration + ((ecData.get(newShape, 'dataIndex') || 0) % 20 * 100),
|
||
|
easing
|
||
|
);
|
||
|
}
|
||
|
else if (newShape.type === 'sector') {
|
||
|
ecAnimation.sector(this.zr, oldShape, newShape, duration, easing);
|
||
|
}
|
||
|
else {
|
||
|
this.zr.addShape(newShape);
|
||
|
}
|
||
|
break;
|
||
|
case 'text' :
|
||
|
ecAnimation.text(this.zr, oldShape, newShape, duration, easing);
|
||
|
break;
|
||
|
case 'polygon' :
|
||
|
if (!isUpdate) {
|
||
|
ecAnimation.polygon(this.zr, oldShape, newShape, duration, easing);
|
||
|
}
|
||
|
else {
|
||
|
ecAnimation.pointList(this.zr, oldShape, newShape, duration, easing);
|
||
|
}
|
||
|
break;
|
||
|
case 'ribbon' :
|
||
|
ecAnimation.ribbon(this.zr, oldShape, newShape, duration, easing);
|
||
|
break;
|
||
|
case 'gauge-pointer' :
|
||
|
ecAnimation.gaugePointer(this.zr, oldShape, newShape, duration, easing);
|
||
|
break;
|
||
|
case 'mark-line' :
|
||
|
ecAnimation.markline(this.zr, oldShape, newShape, duration, easing);
|
||
|
break;
|
||
|
case 'bezier-curve' :
|
||
|
case 'line' :
|
||
|
ecAnimation.line(this.zr, oldShape, newShape, duration, easing);
|
||
|
break;
|
||
|
default :
|
||
|
this.zr.addShape(newShape);
|
||
|
break;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 标注动画
|
||
|
* @param {number} duration 时长
|
||
|
* @param {string=} easing 缓动效果
|
||
|
* @param {Array=} shapeList 指定特效对象,不指定默认使用this.shapeList
|
||
|
*/
|
||
|
animationMark: function (duration , easing, shapeList) {
|
||
|
var shapeList = shapeList || this.shapeList;
|
||
|
for (var i = 0, l = shapeList.length; i < l; i++) {
|
||
|
if (!shapeList[i]._mark) {
|
||
|
continue;
|
||
|
}
|
||
|
this._animateMod(false, shapeList[i], duration, easing, 0, true);
|
||
|
}
|
||
|
this.animationEffect(shapeList);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 特效动画
|
||
|
* @param {Array=} shapeList 指定特效对象,不知道默认使用this.shapeList
|
||
|
*/
|
||
|
animationEffect: function (shapeList) {
|
||
|
!shapeList && this.clearEffectShape();
|
||
|
shapeList = shapeList || this.shapeList;
|
||
|
if (shapeList == null) {
|
||
|
return;
|
||
|
}
|
||
|
var zlevel = ecConfig.EFFECT_ZLEVEL;
|
||
|
if (this.canvasSupported) {
|
||
|
this.zr.modLayer(
|
||
|
zlevel,
|
||
|
{
|
||
|
motionBlur: true,
|
||
|
lastFrameAlpha: 0.95
|
||
|
}
|
||
|
);
|
||
|
}
|
||
|
var shape;
|
||
|
for (var i = 0, l = shapeList.length; i < l; i++) {
|
||
|
shape = shapeList[i];
|
||
|
if (!(shape._mark && shape.effect && shape.effect.show && ecEffect[shape._mark])) {
|
||
|
continue;
|
||
|
}
|
||
|
ecEffect[shape._mark](this.zr, this.effectList, shape, zlevel);
|
||
|
this.effectList[this.effectList.length - 1]._mark = shape._mark;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
clearEffectShape: function (clearMotionBlur) {
|
||
|
var effectList = this.effectList;
|
||
|
if (this.zr && effectList && effectList.length > 0) {
|
||
|
clearMotionBlur && this.zr.modLayer(
|
||
|
ecConfig.EFFECT_ZLEVEL,
|
||
|
{ motionBlur: false }
|
||
|
);
|
||
|
this.zr.delShape(effectList);
|
||
|
|
||
|
// 手动清除不会被 zr 自动清除的动画控制器
|
||
|
for (var i = 0; i < effectList.length; i++) {
|
||
|
if (effectList[i].effectAnimator) {
|
||
|
effectList[i].effectAnimator.stop();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
this.effectList = [];
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 动态标线标注添加
|
||
|
* @param {number} seriesIndex 系列索引
|
||
|
* @param {Object} markData 标线标注对象,支持多个
|
||
|
* @param {string} markType 标线标注类型
|
||
|
*/
|
||
|
addMark: function (seriesIndex, markData, markType) {
|
||
|
var serie = this.series[seriesIndex];
|
||
|
if (this.selectedMap[serie.name]) {
|
||
|
var duration = this.query(this.option, 'animationDurationUpdate');
|
||
|
var easing = this.query(this.option, 'animationEasing');
|
||
|
// 备份,复用_buildMarkX
|
||
|
var oriMarkData = serie[markType].data;
|
||
|
var lastLength = this.shapeList.length;
|
||
|
|
||
|
serie[markType].data = markData.data;
|
||
|
this['_build' + markType.replace('m', 'M')](seriesIndex);
|
||
|
if (this.option.animation && !this.option.renderAsImage) {
|
||
|
// animationMark就会addShape
|
||
|
this.animationMark(duration, easing, this.shapeList.slice(lastLength));
|
||
|
}
|
||
|
else {
|
||
|
for (var i = lastLength, l = this.shapeList.length; i < l; i++) {
|
||
|
this.zr.addShape(this.shapeList[i]);
|
||
|
}
|
||
|
this.zr.refreshNextFrame();
|
||
|
}
|
||
|
// 还原,复用_buildMarkX
|
||
|
serie[markType].data = oriMarkData;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 动态标线标注删除
|
||
|
* @param {number} seriesIndex 系列索引
|
||
|
* @param {string} markName 标线标注名称
|
||
|
* @param {string} markType 标线标注类型
|
||
|
*/
|
||
|
delMark: function (seriesIndex, markName, markType) {
|
||
|
markType = markType.replace('mark', '').replace('large', '').toLowerCase();
|
||
|
var serie = this.series[seriesIndex];
|
||
|
if (this.selectedMap[serie.name]) {
|
||
|
var needRefresh = false;
|
||
|
var shapeList = [this.shapeList, this.effectList];
|
||
|
var len = 2;
|
||
|
while(len--) {
|
||
|
for (var i = 0, l = shapeList[len].length; i < l; i++) {
|
||
|
if (shapeList[len][i]._mark == markType
|
||
|
&& ecData.get(shapeList[len][i], 'seriesIndex') == seriesIndex
|
||
|
&& ecData.get(shapeList[len][i], 'name') == markName
|
||
|
) {
|
||
|
this.zr.delShape(shapeList[len][i].id);
|
||
|
shapeList[len].splice(i, 1);
|
||
|
needRefresh = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
needRefresh && this.zr.refreshNextFrame();
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
zrUtil.inherits(Base, ComponentBase);
|
||
|
|
||
|
return Base;
|
||
|
});
|