天津投入产出系统后端
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.

557 lines
21 KiB

/**
* echarts图表类:K线图
*
* @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
* @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
*
*/
define(function (require) {
var ChartBase = require('./base');
// 图形依赖
var CandleShape = require('../util/shape/Candle');
// 组件依赖
require('../component/axis');
require('../component/grid');
require('../component/dataZoom');
var ecConfig = require('../config');
// K线图默认参数
ecConfig.k = {
zlevel: 0, // 一级层叠
z: 2, // 二级层叠
clickable: true,
hoverable: true,
legendHoverLink: false,
xAxisIndex: 0,
yAxisIndex: 0,
// barWidth: null // 默认自适应
// barMaxWidth: null // 默认自适应
itemStyle: {
normal: {
color: '#fff', // 阳线填充颜色
color0: '#00aa11', // 阴线填充颜色
lineStyle: {
width: 1,
color: '#ff3200', // 阳线边框颜色
color0: '#00aa11' // 阴线边框颜色
},
label: {
show: false
// formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调
// position: 默认自适应,水平布局为'top',垂直布局为'right',可选为
// 'inside'|'left'|'right'|'top'|'bottom'
// textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE
}
},
emphasis: {
// color: 各异,
// color0: 各异,
label: {
show: false
// formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调
// position: 默认自适应,水平布局为'top',垂直布局为'right',可选为
// 'inside'|'left'|'right'|'top'|'bottom'
// textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE
}
}
}
};
var ecData = require('../util/ecData');
var zrUtil = require('zrender/tool/util');
/**
* 构造函数
* @param {Object} messageCenter echart消息中心
* @param {ZRender} zr zrender实例
* @param {Object} series 数据
* @param {Object} component 组件
*/
function K(ecTheme, messageCenter, zr, option, myChart) {
// 图表基类
ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
this.refresh(option);
}
K.prototype = {
type: ecConfig.CHART_TYPE_K,
/**
* 绘制图形
*/
_buildShape: function () {
var series = this.series;
this.selectedMap = {};
// 水平垂直双向series索引 ,position索引到seriesIndex
var _position2sIndexMap = {
top: [],
bottom: []
};
var xAxis;
for (var i = 0, l = series.length; i < l; i++) {
if (series[i].type === ecConfig.CHART_TYPE_K) {
series[i] = this.reformOption(series[i]);
this.legendHoverLink = series[i].legendHoverLink || this.legendHoverLink;
xAxis = this.component.xAxis.getAxis(series[i].xAxisIndex);
if (xAxis.type === ecConfig.COMPONENT_TYPE_AXIS_CATEGORY
) {
_position2sIndexMap[xAxis.getPosition()].push(i);
}
}
}
//console.log(_position2sIndexMap)
for (var position in _position2sIndexMap) {
if (_position2sIndexMap[position].length > 0) {
this._buildSinglePosition(
position, _position2sIndexMap[position]
);
}
}
this.addShapeList();
},
/**
* 构建单个方向上的K线图
*
* @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;
}
this._buildHorizontal(seriesArray, maxDataLength, locationMap);
for (var i = 0, l = seriesArray.length; i < l; i++) {
this.buildMark(seriesArray[i]);
}
},
/**
* 数据整形
* 数组位置映射到系列索引
*/
_mapData: function (seriesArray) {
var series = this.series;
var serie; // 临时映射变量
var serieName; // 临时映射变量
var legend = this.component.legend;
var locationMap = []; // 需要返回的东西:数组位置映射到系列索引
var maxDataLength = 0; // 需要返回的东西:最大数据长度
// 计算需要显示的个数和分配位置并记在下面这个结构里
for (var i = 0, l = seriesArray.length; i < l; i++) {
serie = series[seriesArray[i]];
serieName = serie.name;
this.selectedMap[serieName] = legend
? legend.isSelected(serieName)
: true;
if (this.selectedMap[serieName]) {
locationMap.push(seriesArray[i]);
}
// 兼职帮算一下最大长度
maxDataLength = Math.max(maxDataLength, serie.data.length);
}
return {
locationMap: locationMap,
maxDataLength: maxDataLength
};
},
/**
* 构建类目轴为水平方向的K线图系列
*/
_buildHorizontal: function (seriesArray, maxDataLength, locationMap) {
var series = this.series;
// 确定类目轴和数值轴,同一方向随便找一个即可
var seriesIndex;
var serie;
var xAxisIndex;
var categoryAxis;
var yAxisIndex; // 数值轴各异
var valueAxis; // 数值轴各异
var pointList = {};
var candleWidth;
var data;
var value;
var barMaxWidth;
for (var j = 0, k = locationMap.length; j < k; j++) {
seriesIndex = locationMap[j];
serie = series[seriesIndex];
xAxisIndex = serie.xAxisIndex || 0;
categoryAxis = this.component.xAxis.getAxis(xAxisIndex);
candleWidth = serie.barWidth
|| Math.floor(categoryAxis.getGap() / 2);
barMaxWidth = serie.barMaxWidth;
if (barMaxWidth && barMaxWidth < candleWidth) {
candleWidth = barMaxWidth;
}
yAxisIndex = serie.yAxisIndex || 0;
valueAxis = this.component.yAxis.getAxis(yAxisIndex);
pointList[seriesIndex] = [];
for (var i = 0, l = maxDataLength; i < l; i++) {
if (categoryAxis.getNameByIndex(i) == null) {
// 系列数据超出类目轴长度
break;
}
data = serie.data[i];
value = this.getDataFromOption(data, '-');
if (value === '-' || value.length != 4) {
// 数据格式不符
continue;
}
pointList[seriesIndex].push([
categoryAxis.getCoordByIndex(i), // 横坐标
candleWidth,
valueAxis.getCoord(value[0]), // 纵坐标:开盘
valueAxis.getCoord(value[1]), // 纵坐标:收盘
valueAxis.getCoord(value[2]), // 纵坐标:最低
valueAxis.getCoord(value[3]), // 纵坐标:最高
i, // 数据index
categoryAxis.getNameByIndex(i) // 类目名称
]);
}
}
// console.log(pointList)
this._buildKLine(seriesArray, pointList);
},
/**
* 生成K线
*/
_buildKLine: function (seriesArray, pointList) {
var series = this.series;
// normal:
var nLineWidth;
var nLineColor;
var nLineColor0; // 阴线
var nColor;
var nColor0; // 阴线
// emphasis:
var eLineWidth;
var eLineColor;
var eLineColor0;
var eColor;
var eColor0;
var serie;
var queryTarget;
var data;
var seriesPL;
var singlePoint;
var candleType;
var seriesIndex;
for (var sIdx = 0, len = seriesArray.length; sIdx < len; sIdx++) {
seriesIndex = seriesArray[sIdx];
serie = series[seriesIndex];
seriesPL = pointList[seriesIndex];
if (this._isLarge(seriesPL)) {
seriesPL = this._getLargePointList(seriesPL);
}
if (serie.type === ecConfig.CHART_TYPE_K && seriesPL != null) {
// 多级控制
queryTarget = serie;
nLineWidth = this.query(
queryTarget, 'itemStyle.normal.lineStyle.width'
);
nLineColor = this.query(
queryTarget, 'itemStyle.normal.lineStyle.color'
);
nLineColor0 = this.query(
queryTarget, 'itemStyle.normal.lineStyle.color0'
);
nColor = this.query(
queryTarget, 'itemStyle.normal.color'
);
nColor0 = this.query(
queryTarget, 'itemStyle.normal.color0'
);
eLineWidth = this.query(
queryTarget, 'itemStyle.emphasis.lineStyle.width'
);
eLineColor = this.query(
queryTarget, 'itemStyle.emphasis.lineStyle.color'
);
eLineColor0 = this.query(
queryTarget, 'itemStyle.emphasis.lineStyle.color0'
);
eColor = this.query(
queryTarget, 'itemStyle.emphasis.color'
);
eColor0 = this.query(
queryTarget, 'itemStyle.emphasis.color0'
);
/*
* pointlist=[
* 0 x,
* 1 width,
* 2 y0,
* 3 y1,
* 4 y2,
* 5 y3,
* 6 dataIndex,
* 7 categoryName
* ]
*/
for (var i = 0, l = seriesPL.length; i < l; i++) {
singlePoint = seriesPL[i];
data = serie.data[singlePoint[6]];
queryTarget = data;
candleType = singlePoint[3] < singlePoint[2];
this.shapeList.push(this._getCandle(
seriesIndex, // seriesIndex
singlePoint[6], // dataIndex
singlePoint[7], // name
singlePoint[0], // x
singlePoint[1], // width
singlePoint[2], // y开盘
singlePoint[3], // y收盘
singlePoint[4], // y最低
singlePoint[5], // y最高
// 填充颜色
candleType
? (this.query( // 阳
queryTarget, 'itemStyle.normal.color'
) || nColor)
: (this.query( // 阴
queryTarget, 'itemStyle.normal.color0'
) || nColor0),
// 线宽
this.query(
queryTarget, 'itemStyle.normal.lineStyle.width'
) || nLineWidth,
// 线色
candleType
? (this.query( // 阳
queryTarget,
'itemStyle.normal.lineStyle.color'
) || nLineColor)
: (this.query( // 阴
queryTarget,
'itemStyle.normal.lineStyle.color0'
) || nLineColor0),
//------------高亮
// 填充颜色
candleType
? (this.query( // 阳
queryTarget, 'itemStyle.emphasis.color'
) || eColor || nColor)
: (this.query( // 阴
queryTarget, 'itemStyle.emphasis.color0'
) || eColor0 || nColor0),
// 线宽
this.query(
queryTarget, 'itemStyle.emphasis.lineStyle.width'
) || eLineWidth || nLineWidth,
// 线色
candleType
? (this.query( // 阳
queryTarget,
'itemStyle.emphasis.lineStyle.color'
) || eLineColor || nLineColor)
: (this.query( // 阴
queryTarget,
'itemStyle.emphasis.lineStyle.color0'
) || eLineColor0 || nLineColor0)
));
}
}
}
// console.log(this.shapeList)
},
_isLarge: function(singlePL) {
return singlePL[0][1] < 0.5;
},
/**
* 大规模pointList优化
*/
_getLargePointList: function(singlePL) {
var total = this.component.grid.getWidth();
var len = singlePL.length;
var newList = [];
for (var i = 0; i < total; i++) {
newList[i] = singlePL[Math.floor(len / total * i)];
}
return newList;
},
/**
* 生成K线图上的图形
*/
_getCandle: function (
seriesIndex, dataIndex, name,
x, width, y0, y1, y2, y3,
nColor, nLinewidth, nLineColor,
eColor, eLinewidth, eLineColor
) {
var series = this.series;
var serie = series[seriesIndex];
var data = serie.data[dataIndex];
var queryTarget = [data, serie];
var itemShape = {
zlevel: this.getZlevelBase(),
z: this.getZBase(),
clickable: this.deepQuery(queryTarget, 'clickable'),
hoverable: this.deepQuery(queryTarget, 'hoverable'),
style: {
x: x,
y: [y0, y1, y2, y3],
width: width,
color: nColor,
strokeColor: nLineColor,
lineWidth: nLinewidth,
brushType: 'both'
},
highlightStyle: {
color: eColor,
strokeColor: eLineColor,
lineWidth: eLinewidth
},
_seriesIndex: seriesIndex
};
itemShape = this.addLabel(itemShape, serie, data, name);
ecData.pack(
itemShape,
serie, seriesIndex,
data, dataIndex,
name
);
itemShape = new CandleShape(itemShape);
return itemShape;
},
// 位置转换
getMarkCoord: function (seriesIndex, mpData) {
var serie = this.series[seriesIndex];
var xAxis = this.component.xAxis.getAxis(serie.xAxisIndex);
var yAxis = this.component.yAxis.getAxis(serie.yAxisIndex);
return [
typeof mpData.xAxis != 'string' && xAxis.getCoordByIndex
? xAxis.getCoordByIndex(mpData.xAxis || 0)
: xAxis.getCoord(mpData.xAxis || 0),
typeof mpData.yAxis != 'string' && yAxis.getCoordByIndex
? yAxis.getCoordByIndex(mpData.yAxis || 0)
: yAxis.getCoord(mpData.yAxis || 0)
];
},
/**
* 刷新
*/
refresh: function (newOption) {
if (newOption) {
this.option = newOption;
this.series = newOption.series;
}
this.backupShapeList();
this._buildShape();
},
/**
* 动画设定
*/
addDataAnimation: function (params, done) {
var series = this.series;
var aniMap = {}; // seriesIndex索引参数
for (var i = 0, l = params.length; i < l; i++) {
aniMap[params[i][0]] = params[i];
}
var x;
var dx;
var y;
var serie;
var seriesIndex;
var dataIndex;
var aniCount = 0;
function animationDone() {
aniCount--;
if (aniCount === 0) {
done && done();
}
}
for (var i = 0, l = this.shapeList.length; i < l; i++) {
seriesIndex = this.shapeList[i]._seriesIndex;
if (aniMap[seriesIndex] && !aniMap[seriesIndex][3]) {
// 有数据删除才有移动的动画
if (this.shapeList[i].type === 'candle') {
dataIndex = ecData.get(this.shapeList[i], 'dataIndex');
serie = series[seriesIndex];
if (aniMap[seriesIndex][2]
&& dataIndex === serie.data.length - 1
) {
// 队头加入删除末尾
this.zr.delShape(this.shapeList[i].id);
continue;
}
else if (!aniMap[seriesIndex][2] && dataIndex === 0) {
// 队尾加入删除头部
this.zr.delShape(this.shapeList[i].id);
continue;
}
dx = this.component.xAxis.getAxis(
serie.xAxisIndex || 0
).getGap();
x = aniMap[seriesIndex][2] ? dx : -dx;
y = 0;
aniCount++;
this.zr.animate(this.shapeList[i].id, '')
.when(
this.query(this.option, 'animationDurationUpdate'),
{ position: [ x, y ] }
)
.done(animationDone)
.start();
}
}
}
// 没有动画
if (!aniCount) {
animationDone();
}
}
};
zrUtil.inherits(K, ChartBase);
// 图表注册
require('../chart').define('k', K);
return K;
});