blob: e8ff2330e4c706975f0c30439f6a1883ff4d042e [file] [log] [blame]
/**
* echarts图表类:雷达图
* Copyright 2013 Baidu Inc. All rights reserved.
*
* @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
* @author Neil (杨骥, yangji01@baidu.com)
*
*/
define(function(require) {
/**
* 构造函数
* @param {Object} messageCenter echart消息中心
* @param {ZRender} zr zrender实例
* @param {Object} series 数据
* @param {Object} component 组件
*/
function Radar(messageCenter, zr, option, component) {
// 基类装饰
var ComponentBase = require('../component/base');
ComponentBase.call(this, zr);
// 可计算特性装饰
var CalculableBase = require('./calculableBase');
CalculableBase.call(this, zr, option);
var ecConfig = require('../config');
var ecData = require('../util/ecData');
var zrColor = require('zrender/tool/color');
var self = this;
self.type = ecConfig.CHART_TYPE_RADAR;
var series; // 共享数据源,不要修改跟自己无关的项
var serie;
var _zlevelBase = self.getZlevelBase();
var _queryTarget;
var _dropBoxList;
var _symbol = [
'circle', 'rectangle', 'triangle', 'diamond',
'emptyCircle', 'emptyRectangle', 'emptyTriangle', 'emptyDiamond'
];
var _radarDataCounter;
/**
* 绘制图形
*/
function _buildShape() {
self.selectedMap = {};
_dropBoxList = [];
_radarDataCounter = 0;
for (var i = 0, l = series.length; i < l ; i ++) {
if (series[i].type == ecConfig.CHART_TYPE_RADAR) {
serie = self.reformOption(series[i]);
_queryTarget = [serie, option];
// 添加可拖拽提示框,多系列共用一个极坐标,第一个优先
if (self.deepQuery(_queryTarget, 'calculable')) {
_addDropBox(i);
}
_buildSingleRadar(i);
}
}
for (var i = 0, l = self.shapeList.length; i < l; i++) {
self.shapeList[i].id = zr.newShapeId(self.type);
zr.addShape(self.shapeList[i]);
}
}
/**
* 构建数据图形
* @param {number} 序列的index
*/
function _buildSingleRadar(index) {
var legend = component.legend;
var iconShape;
var data = serie.data;
var defaultColor;
var name;
var pointList;
var calculable = self.deepQuery(_queryTarget, 'calculable');
for (var i = 0; i < data.length; i ++) {
name = data[i].name || '';
// 图例开关
self.selectedMap[name] = legend
? legend.isSelected(name)
: true;
if (!self.selectedMap[name]) {
continue;
}
// 默认颜色策略
if (legend) {
// 有图例则从图例中获取颜色定义
defaultColor = legend.getColor(name);
iconShape = legend.getItemShape(name);
if (iconShape) {
// 回调legend,换一个更形象的icon
iconShape.style.brushType = self.deepQuery(
[data[i], serie], 'itemStyle.normal.areaStyle'
) ? 'both' : 'stroke';
legend.setItemShape(name, iconShape);
}
}
else {
// 全局颜色定义
defaultColor = zr.getColor(i);
}
pointList = _getPointList(serie.polarIndex, data[i]);
// 添加拐点形状
_addSymbol(pointList, defaultColor, data[i], index);
// 添加数据形状
_addDataShape(
pointList, defaultColor, data[i],
index, i, calculable
);
_radarDataCounter++;
}
}
/**
* 获取数据的点集
* @param {number} polarIndex
* @param {Array<Object>} 处理的数据
* @return {Array<Array<number>>} 点集
*/
function _getPointList(polarIndex, dataArr) {
var pointList = [];
var vector;
var polar = component.polar;
for (var i = 0, l = dataArr.value.length; i < l; i++) {
vector = polar.getVector(polarIndex, i, dataArr.value[i]);
if (vector) {
pointList.push(vector);
}
}
return pointList;
}
/**
* 生成折线图上的拐点图形
*/
function _getSymbol(
x, y, symbol, symbolSize, normalColor, emphasisColor, lineWidth
) {
var itemShape = {
shape : 'icon',
zlevel : _zlevelBase + 1,
style : {
iconType : symbol.replace('empty', '').toLowerCase(),
x : x - symbolSize,
y : y - symbolSize,
width : symbolSize * 2,
height : symbolSize * 2,
brushType : 'both',
color : symbol.match('empty') ? '#fff' : normalColor,
strokeColor : normalColor,
lineWidth: lineWidth * 2
},
hoverable: false
};
if (symbol.match('star')) {
itemShape.style.iconType = 'star';
itemShape.style.n =
(symbol.replace('empty', '').replace('star','') - 0) || 5;
}
itemShape._x = x;
itemShape._y = y;
return itemShape;
}
/**
* 添加拐点
* @param {Array<Array<number>>} pointList 点集
* @param {string} defaultColor 默认填充颜色
* @param {object} data 数据
* @param {number} serieIndex
*/
function _addSymbol(pointList, defaultColor, data) {
// 多级控制
var queryTarget = [data, serie];
var symbol = self.deepQuery(queryTarget,'symbol')
|| _symbol[_radarDataCounter % _symbol.length]
|| 'cricle';
if (symbol != 'none') {
var symbolSize = self.deepQuery(queryTarget,'symbolSize');
var nColor = self.deepQuery(
queryTarget, 'itemStyle.normal.color'
);
var eColor = self.deepQuery(
queryTarget, 'itemStyle.emphasis.color'
);
var lineWidth = self.deepQuery(
queryTarget, 'itemStyle.normal.lineStyle.width'
);
for (var i = 0, l = pointList.length; i < l; i++) {
self.shapeList.push(_getSymbol(
pointList[i][0], // x
pointList[i][1], // y
symbol,
symbolSize,
nColor || defaultColor,
eColor || nColor || defaultColor,
lineWidth
));
}
}
}
/**
* 添加数据图形
* @param {Array<Array<number>>} pointList 点集
* @param {string} defaultColor 默认填充颜色
* @param {object} data 数据
* @param {number} serieIndex
* @param {number} dataIndex
* @param {boolean} calcalable
*/
function _addDataShape(
pointList, defaultColor, data,
seriesIndex, dataIndex, calculable
) {
// 多级控制
var queryTarget = [data, serie];
var nColor = self.deepQuery(
queryTarget, 'itemStyle.normal.color'
);
var nLineWidth = self.deepQuery(
queryTarget, 'itemStyle.normal.lineStyle.width'
);
var nLineType = self.deepQuery(
queryTarget, 'itemStyle.normal.lineStyle.type'
);
var nAreaColor = self.deepQuery(
queryTarget, 'itemStyle.normal.areaStyle.color'
);
var nIsAreaFill = self.deepQuery(
queryTarget, 'itemStyle.normal.areaStyle'
);
var shape = {
shape : 'polygon',
zlevel : _zlevelBase,
style : {
pointList : pointList,
brushType : nIsAreaFill ? 'both' : 'stroke',
color : nAreaColor
|| nColor
|| zrColor.alpha(defaultColor,0.5),
strokeColor : nColor || defaultColor,
lineWidth : nLineWidth,
lineType : nLineType
},
highlightStyle : {
brushType : self.deepQuery(
queryTarget,
'itemStyle.emphasis.areaStyle'
) || nIsAreaFill
? 'both' : 'stroke',
color : self.deepQuery(
queryTarget,
'itemStyle.emphasis.areaStyle.color'
)
|| nAreaColor
|| nColor
|| zrColor.alpha(defaultColor,0.5),
strokeColor : self.deepQuery(
queryTarget, 'itemStyle.emphasis.color'
) || nColor || defaultColor,
lineWidth : self.deepQuery(
queryTarget,
'itemStyle.emphasis.lineStyle.width'
) || nLineWidth,
lineType : self.deepQuery(
queryTarget,
'itemStyle.emphasis.lineStyle.type'
) || nLineType
}
};
ecData.pack(
shape,
series[seriesIndex], // 系列
seriesIndex, // 系列索引
data, // 数据
dataIndex, // 数据索引
data.name, // 数据名称
// 附加指标信息
component.polar.getIndicator(series[seriesIndex].polarIndex)
);
if (calculable) {
shape.draggable = true;
self.setCalculable(shape);
}
self.shapeList.push(shape);
}
/**
* 增加外围接受框
* @param {number} serie的序列
*/
function _addDropBox(index) {
var polarIndex = self.deepQuery(
_queryTarget, 'polarIndex'
);
if (!_dropBoxList[polarIndex]) {
var shape = component.polar.getDropBox(polarIndex);
shape.zlevel = _zlevelBase;
self.setCalculable(shape);
ecData.pack(shape, series, index, undefined, -1);
self.shapeList.push(shape);
_dropBoxList[polarIndex] = true;
}
}
/**
* 数据项被拖拽出去,重载基类方法
*/
function ondragend(param, status) {
if (!self.isDragend || !param.target) {
// 没有在当前实例上发生拖拽行为则直接返回
return;
}
var target = param.target; // 被拖拽图形元素
var seriesIndex = ecData.get(target, 'seriesIndex');
var dataIndex = ecData.get(target, 'dataIndex');
// 被拖拽的图形是饼图sector,删除被拖拽走的数据
component.legend && component.legend.del(
series[seriesIndex].data[dataIndex].name
);
series[seriesIndex].data.splice(dataIndex, 1);
// 别status = {}赋值啊!!
status.dragOut = true;
status.needRefresh = true;
// 处理完拖拽事件后复位
self.isDragend = false;
return;
}
/**
* 数据项被拖拽进来, 重载基类方法
*/
function ondrop(param, status) {
if (!self.isDrop || !param.target) {
// 没有在当前实例上发生拖拽行为则直接返回
return;
}
var target = param.target; // 拖拽安放目标
var dragged = param.dragged; // 当前被拖拽的图形对象
var seriesIndex = ecData.get(target, 'seriesIndex');
var dataIndex = ecData.get(target, 'dataIndex');
var data;
var legend = component.legend;
var value;
if (dataIndex == -1) {
data = {
value : ecData.get(dragged, 'value'),
name : ecData.get(dragged, 'name')
};
series[seriesIndex].data.push(data);
legend && legend.add(
data.name,
dragged.style.color || dragged.style.strokeColor
);
}
else {
data = series[seriesIndex].data[dataIndex];
legend && legend.del(data.name);
data.name += option.nameConnector
+ ecData.get(dragged, 'name');
value = ecData.get(dragged, 'value');
for (var i = 0 ; i < value.length; i ++) {
data.value[i] += value[i];
}
legend && legend.add(
data.name,
dragged.style.color || dragged.style.strokeColor
);
}
// 别status = {}赋值啊!!
status.dragIn = status.dragIn || true;
// 处理完拖拽事件后复位
self.isDrop = false;
return;
}
/**
* 构造函数默认执行的初始化方法,也用于创建实例后动态修改
* @param {Object} newZr
* @param {Object} newSeries
* @param {Object} newComponent
*/
function init(newOption, newComponent) {
component = newComponent;
refresh(newOption);
}
/**
* 刷新
*/
function refresh(newOption) {
if (newOption) {
option = newOption;
series = option.series;
}
self.clear();
_buildShape();
}
function animation() {
var duration = self.deepQuery([option], 'animationDuration');
var easing = self.deepQuery([option], 'animationEasing');
var dataIndex;
var seriesIndex;
var data;
var serie;
var polarIndex;
var polar = component.polar;
var center;
var item;
var x;
var y;
for (var i = 0, l = self.shapeList.length; i < l; i++) {
if (self.shapeList[i].shape == 'polygon') {
item = self.shapeList[i];
seriesIndex = ecData.get(item, 'seriesIndex');
dataIndex = ecData.get(item, 'dataIndex');
serie = series[seriesIndex];
data = serie.data[dataIndex];
polarIndex = self.deepQuery(
[data, serie, option], 'polarIndex');
center = polar.getCenter(polarIndex);
x = center[0];
y = center[1];
zr.modShape(self.shapeList[i].id, {
scale : [0.1, 0.1, x, y]
});
zr.animate(item.id, '')
.when(
(self.deepQuery([serie],'animationDuration')
|| duration)
+ dataIndex * 100,
{scale : [1, 1, x, y]},
(self.deepQuery([serie], 'animationEasing')
|| easing)
)
.start();
}
else {
x = self.shapeList[i]._x || 0;
y = self.shapeList[i]._y || 0;
zr.modShape(self.shapeList[i].id, {
scale : [0, 0, x, y]
});
zr.animate(self.shapeList[i].id, '')
.when(
duration,
{scale : [1, 1, x, y]},
'QuinticOut'
)
.start();
}
}
}
self.init = init;
self.refresh = refresh;
self.animation = animation;
self.ondrop = ondrop;
self.ondragend = ondragend;
init(option, component);
}
// 图表注册
require('../chart').define('radar', Radar);
return Radar;
});