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.
778 lines
30 KiB
778 lines
30 KiB
4 years ago
|
/**
|
||
|
* echarts图表类:漏斗图
|
||
|
*
|
||
|
* @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
|
||
|
* @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
|
||
|
*
|
||
|
*/
|
||
|
define(function (require) {
|
||
|
var ChartBase = require('./base');
|
||
|
|
||
|
// 图形依赖
|
||
|
var TextShape = require('zrender/shape/Text');
|
||
|
var LineShape = require('zrender/shape/Line');
|
||
|
var PolygonShape = require('zrender/shape/Polygon');
|
||
|
|
||
|
var ecConfig = require('../config');
|
||
|
// 漏斗图默认参数
|
||
|
ecConfig.funnel = {
|
||
|
zlevel: 0, // 一级层叠
|
||
|
z: 2, // 二级层叠
|
||
|
clickable: true,
|
||
|
legendHoverLink: true,
|
||
|
x: 80,
|
||
|
y: 60,
|
||
|
x2: 80,
|
||
|
y2: 60,
|
||
|
// width: {totalWidth} - x - x2,
|
||
|
// height: {totalHeight} - y - y2,
|
||
|
min: 0,
|
||
|
max: 100,
|
||
|
minSize: '0%',
|
||
|
maxSize: '100%',
|
||
|
sort: 'descending', // 'ascending', 'descending'
|
||
|
gap: 0,
|
||
|
funnelAlign: 'center',
|
||
|
itemStyle: {
|
||
|
normal: {
|
||
|
// color: 各异,
|
||
|
borderColor: '#fff',
|
||
|
borderWidth: 1,
|
||
|
label: {
|
||
|
show: true,
|
||
|
position: 'outer'
|
||
|
// formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调
|
||
|
// textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE
|
||
|
},
|
||
|
labelLine: {
|
||
|
show: true,
|
||
|
length: 10,
|
||
|
lineStyle: {
|
||
|
// color: 各异,
|
||
|
width: 1,
|
||
|
type: 'solid'
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
emphasis: {
|
||
|
// color: 各异,
|
||
|
borderColor: 'rgba(0,0,0,0)',
|
||
|
borderWidth: 1,
|
||
|
label: {
|
||
|
show: true
|
||
|
},
|
||
|
labelLine: {
|
||
|
show: true
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var ecData = require('../util/ecData');
|
||
|
var number = require('../util/number');
|
||
|
var zrUtil = require('zrender/tool/util');
|
||
|
var zrColor = require('zrender/tool/color');
|
||
|
var zrArea = require('zrender/tool/area');
|
||
|
|
||
|
/**
|
||
|
* 构造函数
|
||
|
* @param {Object} messageCenter echart消息中心
|
||
|
* @param {ZRender} zr zrender实例
|
||
|
* @param {Object} series 数据
|
||
|
* @param {Object} component 组件
|
||
|
*/
|
||
|
function Funnel(ecTheme, messageCenter, zr, option, myChart) {
|
||
|
// 图表基类
|
||
|
ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
|
||
|
this.refresh(option);
|
||
|
}
|
||
|
|
||
|
Funnel.prototype = {
|
||
|
type: ecConfig.CHART_TYPE_FUNNEL,
|
||
|
/**
|
||
|
* 绘制图形
|
||
|
*/
|
||
|
_buildShape: function () {
|
||
|
var series = this.series;
|
||
|
var legend = this.component.legend;
|
||
|
// 复用参数索引
|
||
|
this._paramsMap = {};
|
||
|
this._selected = {};
|
||
|
this.selectedMap = {};
|
||
|
|
||
|
var serieName;
|
||
|
for (var i = 0, l = series.length; i < l; i++) {
|
||
|
if (series[i].type === ecConfig.CHART_TYPE_FUNNEL) {
|
||
|
series[i] = this.reformOption(series[i]);
|
||
|
this.legendHoverLink = series[i].legendHoverLink || this.legendHoverLink;
|
||
|
serieName = series[i].name || '';
|
||
|
// 系列图例开关
|
||
|
this.selectedMap[serieName] = legend ? legend.isSelected(serieName) : true;
|
||
|
if (!this.selectedMap[serieName]) {
|
||
|
continue;
|
||
|
}
|
||
|
this._buildSingleFunnel(i);
|
||
|
this.buildMark(i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this.addShapeList();
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 构建单个仪表盘
|
||
|
*
|
||
|
* @param {number} seriesIndex 系列索引
|
||
|
*/
|
||
|
_buildSingleFunnel: function (seriesIndex) {
|
||
|
var legend = this.component.legend;
|
||
|
var serie = this.series[seriesIndex];
|
||
|
var data = this._mapData(seriesIndex);
|
||
|
var location = this._getLocation(seriesIndex);
|
||
|
this._paramsMap[seriesIndex] = {
|
||
|
location: location,
|
||
|
data: data
|
||
|
};
|
||
|
|
||
|
var itemName;
|
||
|
var total = 0;
|
||
|
var selectedData = [];
|
||
|
// 计算需要显示的个数和总值
|
||
|
for (var i = 0, l = data.length; i < l; i++) {
|
||
|
itemName = data[i].name;
|
||
|
this.selectedMap[itemName] = legend ? legend.isSelected(itemName) : true;
|
||
|
|
||
|
if (this.selectedMap[itemName] && !isNaN(data[i].value)) {
|
||
|
selectedData.push(data[i]);
|
||
|
total++;
|
||
|
}
|
||
|
}
|
||
|
if (total === 0) {
|
||
|
return;
|
||
|
}
|
||
|
// 可计算箱子
|
||
|
var funnelCase = this._buildFunnelCase(seriesIndex);
|
||
|
var align = serie.funnelAlign;
|
||
|
var gap = serie.gap;
|
||
|
var height = total > 1
|
||
|
? (location.height - (total - 1) * gap) / total : location.height;
|
||
|
var width;
|
||
|
var lastY = location.y;
|
||
|
var lastWidth = serie.sort === 'descending'
|
||
|
? this._getItemWidth(seriesIndex, selectedData[0].value)
|
||
|
: number.parsePercent(serie.minSize, location.width);
|
||
|
var next = serie.sort === 'descending' ? 1 : 0;
|
||
|
var centerX = location.centerX;
|
||
|
var pointList= [];
|
||
|
var x;
|
||
|
var polygon;
|
||
|
var lastPolygon;
|
||
|
for (var i = 0, l = selectedData.length; i < l; i++) {
|
||
|
itemName = selectedData[i].name;
|
||
|
if (this.selectedMap[itemName] && !isNaN(selectedData[i].value)) {
|
||
|
width = i <= l - 2
|
||
|
? this._getItemWidth(seriesIndex, selectedData[i + next].value)
|
||
|
: serie.sort === 'descending'
|
||
|
? number.parsePercent(serie.minSize, location.width)
|
||
|
: number.parsePercent(serie.maxSize, location.width);
|
||
|
switch (align) {
|
||
|
case 'left':
|
||
|
x = location.x;
|
||
|
break;
|
||
|
case 'right':
|
||
|
x = location.x + location.width - lastWidth;
|
||
|
break;
|
||
|
default:
|
||
|
x = centerX - lastWidth / 2;
|
||
|
}
|
||
|
polygon = this._buildItem(
|
||
|
seriesIndex, selectedData[i]._index,
|
||
|
legend // color
|
||
|
? legend.getColor(itemName)
|
||
|
: this.zr.getColor(selectedData[i]._index),
|
||
|
x, lastY, lastWidth, width, height, align
|
||
|
);
|
||
|
lastY += height + gap;
|
||
|
lastPolygon = polygon.style.pointList;
|
||
|
|
||
|
pointList.unshift([lastPolygon[0][0] - 10, lastPolygon[0][1]]); // 左
|
||
|
pointList.push([lastPolygon[1][0] + 10, lastPolygon[1][1]]); // 右
|
||
|
if (i === 0) {
|
||
|
if (lastWidth === 0) {
|
||
|
lastPolygon = pointList.pop();
|
||
|
align == 'center' && (pointList[0][0] += 10);
|
||
|
align == 'right' && (pointList[0][0] = lastPolygon[0]);
|
||
|
pointList[0][1] -= align == 'center' ? 10 : 15;
|
||
|
if (l == 1) {
|
||
|
lastPolygon = polygon.style.pointList;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
pointList[pointList.length - 1][1] -= 5;
|
||
|
pointList[0][1] -=5;
|
||
|
}
|
||
|
}
|
||
|
lastWidth = width;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (funnelCase) {
|
||
|
pointList.unshift([lastPolygon[3][0] - 10, lastPolygon[3][1]]); // 左
|
||
|
pointList.push([lastPolygon[2][0] + 10, lastPolygon[2][1]]); // 右
|
||
|
if (lastWidth === 0) {
|
||
|
lastPolygon = pointList.pop();
|
||
|
align == 'center' && (pointList[0][0] += 10);
|
||
|
align == 'right' && (pointList[0][0] = lastPolygon[0]);
|
||
|
pointList[0][1] += align == 'center' ? 10 : 15;
|
||
|
}
|
||
|
else {
|
||
|
pointList[pointList.length - 1][1] += 5;
|
||
|
pointList[0][1] +=5;
|
||
|
}
|
||
|
funnelCase.style.pointList = pointList;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_buildFunnelCase: function(seriesIndex) {
|
||
|
var serie = this.series[seriesIndex];
|
||
|
if (this.deepQuery([serie, this.option], 'calculable')) {
|
||
|
var location = this._paramsMap[seriesIndex].location;
|
||
|
var gap = 10;
|
||
|
var funnelCase = {
|
||
|
hoverable: false,
|
||
|
style: {
|
||
|
pointListd: [
|
||
|
[location.x - gap, location.y - gap],
|
||
|
[location.x + location.width + gap, location.y - gap],
|
||
|
[location.x + location.width + gap, location.y + location.height + gap],
|
||
|
[location.x - gap, location.y + location.height + gap]
|
||
|
],
|
||
|
brushType: 'stroke',
|
||
|
lineWidth: 1,
|
||
|
strokeColor: serie.calculableHolderColor
|
||
|
|| this.ecTheme.calculableHolderColor
|
||
|
|| ecConfig.calculableHolderColor
|
||
|
}
|
||
|
};
|
||
|
ecData.pack(funnelCase, serie, seriesIndex, undefined, -1);
|
||
|
this.setCalculable(funnelCase);
|
||
|
funnelCase = new PolygonShape(funnelCase);
|
||
|
this.shapeList.push(funnelCase);
|
||
|
return funnelCase;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_getLocation: function (seriesIndex) {
|
||
|
var gridOption = this.series[seriesIndex];
|
||
|
var zrWidth = this.zr.getWidth();
|
||
|
var zrHeight = this.zr.getHeight();
|
||
|
var x = this.parsePercent(gridOption.x, zrWidth);
|
||
|
var y = this.parsePercent(gridOption.y, zrHeight);
|
||
|
|
||
|
var width = gridOption.width == null
|
||
|
? (zrWidth - x - this.parsePercent(gridOption.x2, zrWidth))
|
||
|
: this.parsePercent(gridOption.width, zrWidth);
|
||
|
|
||
|
return {
|
||
|
x: x,
|
||
|
y: y,
|
||
|
width: width,
|
||
|
height: gridOption.height == null
|
||
|
? (zrHeight - y - this.parsePercent(gridOption.y2, zrHeight))
|
||
|
: this.parsePercent(gridOption.height, zrHeight),
|
||
|
centerX: x + width / 2
|
||
|
};
|
||
|
},
|
||
|
|
||
|
_mapData: function(seriesIndex) {
|
||
|
var serie = this.series[seriesIndex];
|
||
|
var funnelData = zrUtil.clone(serie.data);
|
||
|
for (var i = 0, l = funnelData.length; i < l; i++) {
|
||
|
funnelData[i]._index = i;
|
||
|
}
|
||
|
function numDescending (a, b) {
|
||
|
if (a.value === '-') {
|
||
|
return 1;
|
||
|
}
|
||
|
else if (b.value === '-') {
|
||
|
return -1;
|
||
|
}
|
||
|
return b.value - a.value;
|
||
|
}
|
||
|
function numAscending (a, b) {
|
||
|
return -numDescending(a, b);
|
||
|
}
|
||
|
if (serie.sort != 'none') {
|
||
|
funnelData.sort(serie.sort === 'descending' ? numDescending : numAscending);
|
||
|
}
|
||
|
|
||
|
return funnelData;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 构建单个扇形及指标
|
||
|
*/
|
||
|
_buildItem: function (
|
||
|
seriesIndex, dataIndex, defaultColor,
|
||
|
x, y, topWidth, bottomWidth, height, align
|
||
|
) {
|
||
|
var series = this.series;
|
||
|
var serie = series[seriesIndex];
|
||
|
var data = serie.data[dataIndex];
|
||
|
|
||
|
// 漏斗
|
||
|
var polygon = this.getPolygon(
|
||
|
seriesIndex, dataIndex, defaultColor,
|
||
|
x, y, topWidth, bottomWidth, height, align
|
||
|
);
|
||
|
ecData.pack(
|
||
|
polygon,
|
||
|
series[seriesIndex], seriesIndex,
|
||
|
series[seriesIndex].data[dataIndex], dataIndex,
|
||
|
series[seriesIndex].data[dataIndex].name
|
||
|
);
|
||
|
this.shapeList.push(polygon);
|
||
|
|
||
|
// 文本标签
|
||
|
var label = this.getLabel(
|
||
|
seriesIndex, dataIndex, defaultColor,
|
||
|
x, y, topWidth, bottomWidth, height, align
|
||
|
);
|
||
|
ecData.pack(
|
||
|
label,
|
||
|
series[seriesIndex], seriesIndex,
|
||
|
series[seriesIndex].data[dataIndex], dataIndex,
|
||
|
series[seriesIndex].data[dataIndex].name
|
||
|
);
|
||
|
this.shapeList.push(label);
|
||
|
// 特定状态下是否需要显示文本标签
|
||
|
if (!this._needLabel(serie, data,false)) {
|
||
|
label.invisible = true;
|
||
|
}
|
||
|
|
||
|
// 文本标签视觉引导线
|
||
|
var labelLine = this.getLabelLine(
|
||
|
seriesIndex, dataIndex, defaultColor,
|
||
|
x, y, topWidth, bottomWidth, height, align
|
||
|
);
|
||
|
this.shapeList.push(labelLine);
|
||
|
// 特定状态下是否需要显示文本标签引导线
|
||
|
if (!this._needLabelLine(serie, data,false)) {
|
||
|
labelLine.invisible = true;
|
||
|
}
|
||
|
|
||
|
var polygonHoverConnect = [];
|
||
|
var labelHoverConnect = [];
|
||
|
if (this._needLabelLine(serie, data, true)) {
|
||
|
polygonHoverConnect.push(labelLine.id);
|
||
|
labelHoverConnect.push(labelLine.id);
|
||
|
}
|
||
|
if (this._needLabel(serie, data, true)) {
|
||
|
polygonHoverConnect.push(label.id);
|
||
|
labelHoverConnect.push(polygon.id);
|
||
|
}
|
||
|
polygon.hoverConnect = polygonHoverConnect;
|
||
|
label.hoverConnect = labelHoverConnect;
|
||
|
|
||
|
return polygon;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 根据值计算宽度
|
||
|
*/
|
||
|
_getItemWidth: function (seriesIndex, value) {
|
||
|
var serie = this.series[seriesIndex];
|
||
|
var location = this._paramsMap[seriesIndex].location;
|
||
|
var min = serie.min;
|
||
|
var max = serie.max;
|
||
|
var minSize = number.parsePercent(serie.minSize, location.width);
|
||
|
var maxSize = number.parsePercent(serie.maxSize, location.width);
|
||
|
return (value - min) * (maxSize - minSize) / (max - min) + minSize;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 构建扇形
|
||
|
*/
|
||
|
getPolygon: function (
|
||
|
seriesIndex, dataIndex, defaultColor,
|
||
|
xLT, y, topWidth, bottomWidth, height, align
|
||
|
) {
|
||
|
var serie = this.series[seriesIndex];
|
||
|
var data = serie.data[dataIndex];
|
||
|
var queryTarget = [data, serie];
|
||
|
|
||
|
// 多级控制
|
||
|
var normal = this.deepMerge(queryTarget, 'itemStyle.normal') || {};
|
||
|
var emphasis = this.deepMerge(queryTarget,'itemStyle.emphasis') || {};
|
||
|
|
||
|
var normalColor = this.getItemStyleColor(normal.color, seriesIndex, dataIndex, data)
|
||
|
|| defaultColor;
|
||
|
|
||
|
var emphasisColor = this.getItemStyleColor(emphasis.color, seriesIndex, dataIndex, data)
|
||
|
|| (typeof normalColor === 'string'
|
||
|
? zrColor.lift(normalColor, -0.2)
|
||
|
: normalColor
|
||
|
);
|
||
|
|
||
|
var xLB;
|
||
|
switch (align) {
|
||
|
case 'left':
|
||
|
xLB = xLT;
|
||
|
break;
|
||
|
case 'right':
|
||
|
xLB = xLT + (topWidth - bottomWidth);
|
||
|
break;
|
||
|
default:
|
||
|
xLB = xLT + (topWidth - bottomWidth) / 2;
|
||
|
break;
|
||
|
}
|
||
|
var polygon = {
|
||
|
zlevel: this.getZlevelBase(),
|
||
|
z: this.getZBase(),
|
||
|
clickable: this.deepQuery(queryTarget, 'clickable'),
|
||
|
style: {
|
||
|
pointList: [
|
||
|
[xLT, y],
|
||
|
[xLT + topWidth, y],
|
||
|
[xLB + bottomWidth, y + height],
|
||
|
[xLB, y + height]
|
||
|
],
|
||
|
brushType: 'both',
|
||
|
color: normalColor,
|
||
|
lineWidth: normal.borderWidth,
|
||
|
strokeColor: normal.borderColor
|
||
|
},
|
||
|
highlightStyle: {
|
||
|
color: emphasisColor,
|
||
|
lineWidth: emphasis.borderWidth,
|
||
|
strokeColor: emphasis.borderColor
|
||
|
}
|
||
|
};
|
||
|
|
||
|
if (this.deepQuery([data, serie, this.option], 'calculable')) {
|
||
|
this.setCalculable(polygon);
|
||
|
polygon.draggable = true;
|
||
|
}
|
||
|
|
||
|
return new PolygonShape(polygon);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 需要显示则会有返回构建好的shape,否则返回undefined
|
||
|
*/
|
||
|
getLabel: function (
|
||
|
seriesIndex, dataIndex, defaultColor,
|
||
|
x, y, topWidth, bottomWidth, height, align
|
||
|
) {
|
||
|
var serie = this.series[seriesIndex];
|
||
|
var data = serie.data[dataIndex];
|
||
|
var location = this._paramsMap[seriesIndex].location;
|
||
|
// serie里有默认配置,放心大胆的用!
|
||
|
var itemStyle = zrUtil.merge(
|
||
|
zrUtil.clone(data.itemStyle) || {},
|
||
|
serie.itemStyle
|
||
|
);
|
||
|
var status = 'normal';
|
||
|
// label配置
|
||
|
var labelControl = itemStyle[status].label;
|
||
|
var textStyle = labelControl.textStyle || {};
|
||
|
var lineLength = itemStyle[status].labelLine.length;
|
||
|
|
||
|
var text = this.getLabelText(seriesIndex, dataIndex, status);
|
||
|
var textFont = this.getFont(textStyle);
|
||
|
var textAlign;
|
||
|
var textColor = defaultColor;
|
||
|
labelControl.position = labelControl.position
|
||
|
|| itemStyle.normal.label.position;
|
||
|
if (labelControl.position === 'inner'
|
||
|
|| labelControl.position === 'inside'
|
||
|
|| labelControl.position === 'center'
|
||
|
) {
|
||
|
// 内部
|
||
|
textAlign = align;
|
||
|
textColor =
|
||
|
Math.max(topWidth, bottomWidth) / 2 > zrArea.getTextWidth(text, textFont)
|
||
|
? '#fff' : zrColor.reverse(defaultColor);
|
||
|
}
|
||
|
else if (labelControl.position === 'left'){
|
||
|
// 左侧显示
|
||
|
textAlign = 'right';
|
||
|
}
|
||
|
else {
|
||
|
// 右侧显示,默认 labelControl.position === 'outer' || 'right)
|
||
|
textAlign = 'left';
|
||
|
}
|
||
|
|
||
|
var textShape = {
|
||
|
zlevel: this.getZlevelBase(),
|
||
|
z: this.getZBase() + 1,
|
||
|
style: {
|
||
|
x: this._getLabelPoint(
|
||
|
labelControl.position, x, location,
|
||
|
topWidth, bottomWidth,lineLength, align
|
||
|
),
|
||
|
y: y + height / 2,
|
||
|
color: textStyle.color || textColor,
|
||
|
text: text,
|
||
|
textAlign: textStyle.align || textAlign,
|
||
|
textBaseline: textStyle.baseline || 'middle',
|
||
|
textFont: textFont
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//----------高亮
|
||
|
status = 'emphasis';
|
||
|
// label配置
|
||
|
labelControl = itemStyle[status].label || labelControl;
|
||
|
textStyle = labelControl.textStyle || textStyle;
|
||
|
lineLength = itemStyle[status].labelLine.length || lineLength;
|
||
|
labelControl.position = labelControl.position || itemStyle.normal.label.position;
|
||
|
text = this.getLabelText(seriesIndex, dataIndex, status);
|
||
|
textFont = this.getFont(textStyle);
|
||
|
textColor = defaultColor;
|
||
|
if (labelControl.position === 'inner'
|
||
|
|| labelControl.position === 'inside'
|
||
|
|| labelControl.position === 'center'
|
||
|
) {
|
||
|
// 内部
|
||
|
textAlign = align;
|
||
|
textColor =
|
||
|
Math.max(topWidth, bottomWidth) / 2 > zrArea.getTextWidth(text, textFont)
|
||
|
? '#fff' : zrColor.reverse(defaultColor);
|
||
|
}
|
||
|
else if (labelControl.position === 'left'){
|
||
|
// 左侧显示
|
||
|
textAlign = 'right';
|
||
|
}
|
||
|
else {
|
||
|
// 右侧显示,默认 labelControl.position === 'outer' || 'right)
|
||
|
textAlign = 'left';
|
||
|
}
|
||
|
|
||
|
textShape.highlightStyle = {
|
||
|
x: this._getLabelPoint(
|
||
|
labelControl.position, x, location,
|
||
|
topWidth, bottomWidth,lineLength, align
|
||
|
),
|
||
|
color: textStyle.color || textColor,
|
||
|
text: text,
|
||
|
textAlign: textStyle.align || textAlign,
|
||
|
textFont: textFont,
|
||
|
brushType: 'fill'
|
||
|
};
|
||
|
|
||
|
return new TextShape(textShape);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 根据lable.format计算label text
|
||
|
*/
|
||
|
getLabelText: function (seriesIndex, dataIndex, status) {
|
||
|
var series = this.series;
|
||
|
var serie = series[seriesIndex];
|
||
|
var data = serie.data[dataIndex];
|
||
|
var formatter = this.deepQuery(
|
||
|
[data, serie],
|
||
|
'itemStyle.' + status + '.label.formatter'
|
||
|
);
|
||
|
|
||
|
if (formatter) {
|
||
|
if (typeof formatter === 'function') {
|
||
|
return formatter.call(
|
||
|
this.myChart,
|
||
|
{
|
||
|
seriesIndex: seriesIndex,
|
||
|
seriesName: serie.name || '',
|
||
|
series: serie,
|
||
|
dataIndex: dataIndex,
|
||
|
data: data,
|
||
|
name: data.name,
|
||
|
value: data.value
|
||
|
}
|
||
|
);
|
||
|
}
|
||
|
else if (typeof formatter === 'string') {
|
||
|
formatter = formatter.replace('{a}','{a0}')
|
||
|
.replace('{b}','{b0}')
|
||
|
.replace('{c}','{c0}')
|
||
|
.replace('{a0}', serie.name)
|
||
|
.replace('{b0}', data.name)
|
||
|
.replace('{c0}', data.value);
|
||
|
|
||
|
return formatter;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
return data.name;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 需要显示则会有返回构建好的shape,否则返回undefined
|
||
|
*/
|
||
|
getLabelLine: function (
|
||
|
seriesIndex, dataIndex, defaultColor,
|
||
|
x, y, topWidth, bottomWidth, height, align
|
||
|
) {
|
||
|
var serie = this.series[seriesIndex];
|
||
|
var data = serie.data[dataIndex];
|
||
|
var location = this._paramsMap[seriesIndex].location;
|
||
|
|
||
|
// serie里有默认配置,放心大胆的用!
|
||
|
var itemStyle = zrUtil.merge(
|
||
|
zrUtil.clone(data.itemStyle) || {},
|
||
|
serie.itemStyle
|
||
|
);
|
||
|
var status = 'normal';
|
||
|
// labelLine配置
|
||
|
var labelLineControl = itemStyle[status].labelLine;
|
||
|
var lineLength = itemStyle[status].labelLine.length;
|
||
|
var lineStyle = labelLineControl.lineStyle || {};
|
||
|
|
||
|
var labelControl = itemStyle[status].label;
|
||
|
labelControl.position = labelControl.position
|
||
|
|| itemStyle.normal.label.position;
|
||
|
|
||
|
var lineShape = {
|
||
|
zlevel: this.getZlevelBase(),
|
||
|
z: this.getZBase() + 1,
|
||
|
hoverable: false,
|
||
|
style: {
|
||
|
xStart: this._getLabelLineStartPoint(x, location, topWidth, bottomWidth, align),
|
||
|
yStart: y + height / 2,
|
||
|
xEnd: this._getLabelPoint(
|
||
|
labelControl.position, x, location,
|
||
|
topWidth, bottomWidth,lineLength, align
|
||
|
),
|
||
|
yEnd: y + height / 2,
|
||
|
strokeColor: lineStyle.color || defaultColor,
|
||
|
lineType: lineStyle.type,
|
||
|
lineWidth: lineStyle.width
|
||
|
}
|
||
|
};
|
||
|
|
||
|
status = 'emphasis';
|
||
|
// labelLine配置
|
||
|
labelLineControl = itemStyle[status].labelLine || labelLineControl;
|
||
|
lineLength = itemStyle[status].labelLine.length || lineLength;
|
||
|
lineStyle = labelLineControl.lineStyle || lineStyle;
|
||
|
|
||
|
labelControl = itemStyle[status].label || labelControl;
|
||
|
labelControl.position = labelControl.position;
|
||
|
|
||
|
lineShape.highlightStyle = {
|
||
|
xEnd: this._getLabelPoint(
|
||
|
labelControl.position, x, location,
|
||
|
topWidth, bottomWidth,lineLength, align
|
||
|
),
|
||
|
strokeColor: lineStyle.color || defaultColor,
|
||
|
lineType: lineStyle.type,
|
||
|
lineWidth: lineStyle.width
|
||
|
};
|
||
|
|
||
|
return new LineShape(lineShape);
|
||
|
},
|
||
|
|
||
|
_getLabelPoint: function(position, x, location, topWidth, bottomWidth, lineLength, align) {
|
||
|
position = (position === 'inner' || position === 'inside') ? 'center' : position;
|
||
|
switch (position) {
|
||
|
case 'center':
|
||
|
return align == 'center'
|
||
|
? (x + topWidth / 2)
|
||
|
: align == 'left' ? (x + 10) : (x + topWidth - 10);
|
||
|
case 'left':
|
||
|
// 左侧文本
|
||
|
if (lineLength === 'auto') {
|
||
|
return location.x - 10;
|
||
|
}
|
||
|
else {
|
||
|
return align == 'center'
|
||
|
// 居中布局
|
||
|
? (location.centerX - Math.max(topWidth, bottomWidth) / 2 - lineLength)
|
||
|
: align == 'right'
|
||
|
// 右对齐布局
|
||
|
? (x
|
||
|
- (topWidth < bottomWidth ? (bottomWidth - topWidth) : 0)
|
||
|
- lineLength
|
||
|
)
|
||
|
// 左对齐布局
|
||
|
: (location.x - lineLength);
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
// 右侧文本
|
||
|
if (lineLength === 'auto') {
|
||
|
return location.x + location.width + 10;
|
||
|
}
|
||
|
else {
|
||
|
return align == 'center'
|
||
|
// 居中布局
|
||
|
? (location.centerX + Math.max(topWidth, bottomWidth) / 2 + lineLength)
|
||
|
: align == 'right'
|
||
|
// 右对齐布局
|
||
|
? (location.x + location.width + lineLength)
|
||
|
// 左对齐布局
|
||
|
: (x + Math.max(topWidth, bottomWidth) + lineLength);
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_getLabelLineStartPoint: function(x, location, topWidth, bottomWidth, align) {
|
||
|
return align == 'center'
|
||
|
? location.centerX
|
||
|
: topWidth < bottomWidth
|
||
|
? (x + Math.min(topWidth, bottomWidth) / 2)
|
||
|
: (x + Math.max(topWidth, bottomWidth) / 2);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 返回特定状态(normal or emphasis)下是否需要显示label标签文本
|
||
|
* @param {Object} serie
|
||
|
* @param {Object} data
|
||
|
* @param {boolean} isEmphasis true is 'emphasis' and false is 'normal'
|
||
|
*/
|
||
|
_needLabel: function (serie, data, isEmphasis) {
|
||
|
return this.deepQuery(
|
||
|
[data, serie],
|
||
|
'itemStyle.'
|
||
|
+ (isEmphasis ? 'emphasis' : 'normal')
|
||
|
+ '.label.show'
|
||
|
);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 返回特定状态(normal or emphasis)下是否需要显示labelLine标签视觉引导线
|
||
|
* @param {Object} serie
|
||
|
* @param {Object} data
|
||
|
* @param {boolean} isEmphasis true is 'emphasis' and false is 'normal'
|
||
|
*/
|
||
|
_needLabelLine: function (serie, data, isEmphasis) {
|
||
|
return this.deepQuery(
|
||
|
[data, serie],
|
||
|
'itemStyle.'
|
||
|
+ (isEmphasis ? 'emphasis' : 'normal')
|
||
|
+'.labelLine.show'
|
||
|
);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 刷新
|
||
|
*/
|
||
|
refresh: function (newOption) {
|
||
|
if (newOption) {
|
||
|
this.option = newOption;
|
||
|
this.series = newOption.series;
|
||
|
}
|
||
|
|
||
|
this.backupShapeList();
|
||
|
this._buildShape();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
zrUtil.inherits(Funnel, ChartBase);
|
||
|
|
||
|
// 图表注册
|
||
|
require('../chart').define('funnel', Funnel);
|
||
|
|
||
|
return Funnel;
|
||
|
});
|