blob: e023883cc31d9df8dd99aec40f384126bde348e3 [file] [log] [blame]
/**
* echarts图表类:仪表盘
*
* @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
* @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
*
*/
define(function (require) {
var ChartBase = require('./base');
// 图形依赖
var GaugePointerShape = require('../util/shape/GaugePointer');
var TextShape = require('zrender/shape/Text');
var LineShape = require('zrender/shape/Line');
var RectangleShape = require('zrender/shape/Rectangle');
var CircleShape = require('zrender/shape/Circle');
var SectorShape = require('zrender/shape/Sector');
var ecConfig = require('../config');
// 仪表盘默认参数
ecConfig.gauge = {
zlevel: 0, // 一级层叠
z: 2, // 二级层叠
center: ['50%', '50%'], // 默认全局居中
clickable: true,
legendHoverLink: true,
radius: '75%',
startAngle: 225,
endAngle: -45,
min: 0, // 最小值
max: 100, // 最大值
precision: 0, // 小数精度,默认为0,无小数点
splitNumber: 10, // 分割段数,默认为10
axisLine: { // 坐标轴线
show: true, // 默认显示,属性show控制显示与否
lineStyle: { // 属性lineStyle控制线条样式
color: [[0.2, '#228b22'],[0.8, '#48b'],[1, '#ff4500']],
width: 30
}
},
axisTick: { // 坐标轴小标记
show: true, // 属性show控制显示与否,默认不显示
splitNumber: 5, // 每份split细分多少段
length :8, // 属性length控制线长
lineStyle: { // 属性lineStyle控制线条样式
color: '#eee',
width: 1,
type: 'solid'
}
},
axisLabel: { // 坐标轴文本标签,详见axis.axisLabel
show: true,
// formatter: null,
textStyle: { // 其余属性默认使用全局文本样式,详见TEXTSTYLE
color: 'auto'
}
},
splitLine: { // 分隔线
show: true, // 默认显示,属性show控制显示与否
length :30, // 属性length控制线长
lineStyle: { // 属性lineStyle(详见lineStyle)控制线条样式
color: '#eee',
width: 2,
type: 'solid'
}
},
pointer: {
show: true,
length: '80%',
width: 8,
color: 'auto'
},
title: {
show: true,
offsetCenter: [0, '-40%'], // x, y,单位px
textStyle: { // 其余属性默认使用全局文本样式,详见TEXTSTYLE
color: '#333',
fontSize: 15
}
},
detail: {
show: true,
backgroundColor: 'rgba(0,0,0,0)',
borderWidth: 0,
borderColor: '#ccc',
width: 100,
height: 40,
offsetCenter: [0, '40%'], // x, y,单位px
// formatter: null,
textStyle: { // 其余属性默认使用全局文本样式,详见TEXTSTYLE
color: 'auto',
fontSize: 30
}
}
};
var ecData = require('../util/ecData');
var accMath = require('../util/accMath');
var zrUtil = require('zrender/tool/util');
/**
* 构造函数
* @param {Object} messageCenter echart消息中心
* @param {ZRender} zr zrender实例
* @param {Object} series 数据
* @param {Object} component 组件
*/
function Gauge(ecTheme, messageCenter, zr, option, myChart) {
// 图表基类
ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
this.refresh(option);
}
Gauge.prototype = {
type: ecConfig.CHART_TYPE_GAUGE,
/**
* 绘制图形
*/
_buildShape: function () {
var series = this.series;
// 复用参数索引
this._paramsMap = {};
for (var i = 0, l = series.length; i < l; i++) {
if (series[i].type === ecConfig.CHART_TYPE_GAUGE) {
series[i] = this.reformOption(series[i]);
this.legendHoverLink = series[i].legendHoverLink || this.legendHoverLink;
this._buildSingleGauge(i);
this.buildMark(i);
}
}
this.addShapeList();
},
/**
* 构建单个仪表盘
*
* @param {number} seriesIndex 系列索引
*/
_buildSingleGauge: function (seriesIndex) {
var serie = this.series[seriesIndex];
this._paramsMap[seriesIndex] = {
center: this.parseCenter(this.zr, serie.center),
radius: this.parseRadius(this.zr, serie.radius),
startAngle: serie.startAngle.toFixed(2) - 0,
endAngle: serie.endAngle.toFixed(2) - 0
};
this._paramsMap[seriesIndex].totalAngle = this._paramsMap[seriesIndex].startAngle
- this._paramsMap[seriesIndex].endAngle;
this._colorMap(seriesIndex);
this._buildAxisLine(seriesIndex);
this._buildSplitLine(seriesIndex);
this._buildAxisTick(seriesIndex);
this._buildAxisLabel(seriesIndex);
this._buildPointer(seriesIndex);
this._buildTitle(seriesIndex);
this._buildDetail(seriesIndex);
},
// 轴线
_buildAxisLine: function (seriesIndex) {
var serie = this.series[seriesIndex];
if (!serie.axisLine.show) {
return;
}
var min = serie.min;
var total = serie.max - min;
var params = this._paramsMap[seriesIndex];
var center = params.center;
var startAngle = params.startAngle;
var totalAngle = params.totalAngle;
var colorArray = params.colorArray;
var lineStyle = serie.axisLine.lineStyle;
var lineWidth = this.parsePercent(lineStyle.width, params.radius[1]);
var r = params.radius[1];
var r0 = r - lineWidth;
var sectorShape;
var lastAngle = startAngle;
var newAngle;
for (var i = 0, l = colorArray.length; i < l; i++) {
newAngle = startAngle - totalAngle * (colorArray[i][0] - min) / total;
sectorShape = this._getSector(
center, r0, r,
newAngle, // startAngle
lastAngle, // endAngle
colorArray[i][1], // color
lineStyle
);
lastAngle = newAngle;
sectorShape._animationAdd = 'r';
ecData.set(sectorShape, 'seriesIndex', seriesIndex);
ecData.set(sectorShape, 'dataIndex', i);
this.shapeList.push(sectorShape);
}
},
// 坐标轴分割线
_buildSplitLine: function (seriesIndex) {
var serie = this.series[seriesIndex];
if (!serie.splitLine.show) {
return;
}
var params = this._paramsMap[seriesIndex];
var splitNumber = serie.splitNumber;
var min = serie.min;
var total = serie.max - min;
var splitLine = serie.splitLine;
var length = this.parsePercent(splitLine.length, params.radius[1]);
var lineStyle = splitLine.lineStyle;
var color = lineStyle.color;
var center = params.center;
var startAngle = params.startAngle * Math.PI / 180;
var totalAngle = params.totalAngle * Math.PI / 180;
var r = params.radius[1];
var r0 = r - length;
var angle;
var sinAngle;
var cosAngle;
for (var i = 0; i <= splitNumber; i++) {
angle = startAngle - totalAngle / splitNumber * i;
sinAngle = Math.sin(angle);
cosAngle = Math.cos(angle);
this.shapeList.push(new LineShape({
zlevel: this.getZlevelBase(),
z: this.getZBase() + 1,
hoverable: false,
style: {
xStart: center[0] + cosAngle * r,
yStart: center[1] - sinAngle * r,
xEnd: center[0] + cosAngle * r0,
yEnd: center[1] - sinAngle * r0,
strokeColor: color === 'auto'
? this._getColor(seriesIndex, min + total / splitNumber * i)
: color,
lineType: lineStyle.type,
lineWidth: lineStyle.width,
shadowColor: lineStyle.shadowColor,
shadowBlur: lineStyle.shadowBlur,
shadowOffsetX: lineStyle.shadowOffsetX,
shadowOffsetY: lineStyle.shadowOffsetY
}
}));
}
},
// 小标记
_buildAxisTick: function (seriesIndex) {
var serie = this.series[seriesIndex];
if (!serie.axisTick.show) {
return;
}
var params = this._paramsMap[seriesIndex];
var splitNumber = serie.splitNumber;
var min = serie.min;
var total = serie.max - min;
var axisTick = serie.axisTick;
var tickSplit = axisTick.splitNumber;
var length = this.parsePercent(axisTick.length, params.radius[1]);
var lineStyle = axisTick.lineStyle;
var color = lineStyle.color;
var center = params.center;
var startAngle = params.startAngle * Math.PI / 180;
var totalAngle = params.totalAngle * Math.PI / 180;
var r = params.radius[1];
var r0 = r - length;
var angle;
var sinAngle;
var cosAngle;
for (var i = 0, l = splitNumber * tickSplit; i <= l; i++) {
if (i % tickSplit === 0) { // 同splitLine
continue;
}
angle = startAngle - totalAngle / l * i;
sinAngle = Math.sin(angle);
cosAngle = Math.cos(angle);
this.shapeList.push(new LineShape({
zlevel: this.getZlevelBase(),
z: this.getZBase() + 1,
hoverable: false,
style: {
xStart: center[0] + cosAngle * r,
yStart: center[1] - sinAngle * r,
xEnd: center[0] + cosAngle * r0,
yEnd: center[1] - sinAngle * r0,
strokeColor: color === 'auto'
? this._getColor(seriesIndex, min + total / l * i)
: color,
lineType: lineStyle.type,
lineWidth: lineStyle.width,
shadowColor: lineStyle.shadowColor,
shadowBlur: lineStyle.shadowBlur,
shadowOffsetX: lineStyle.shadowOffsetX,
shadowOffsetY: lineStyle.shadowOffsetY
}
}));
}
},
// 坐标轴文本
_buildAxisLabel: function (seriesIndex) {
var serie = this.series[seriesIndex];
if (!serie.axisLabel.show) {
return;
}
var splitNumber = serie.splitNumber;
var min = serie.min;
var total = serie.max - min;
var textStyle = serie.axisLabel.textStyle;
var textFont = this.getFont(textStyle);
var color = textStyle.color;
var params = this._paramsMap[seriesIndex];
var center = params.center;
var startAngle = params.startAngle;
var totalAngle = params.totalAngle;
var r0 = params.radius[1]
- this.parsePercent(serie.splitLine.length, params.radius[1])
- 5;
var angle;
var sinAngle;
var cosAngle;
var value;
for (var i = 0; i <= splitNumber; i++) {
value = accMath.accAdd(
min , accMath.accMul(accMath.accDiv(total , splitNumber), i)
);
angle = startAngle - totalAngle / splitNumber * i;
sinAngle = Math.sin(angle * Math.PI / 180);
cosAngle = Math.cos(angle * Math.PI / 180);
angle = (angle + 360) % 360;
this.shapeList.push(new TextShape({
zlevel: this.getZlevelBase(),
z: this.getZBase() + 1,
hoverable: false,
style: {
x: center[0] + cosAngle * r0,
y: center[1] - sinAngle * r0,
color: color === 'auto' ? this._getColor(seriesIndex, value) : color,
text: this._getLabelText(serie.axisLabel.formatter, value),
textAlign: (angle >= 110 && angle <= 250)
? 'left'
: (angle <= 70 || angle >= 290)
? 'right'
: 'center',
textBaseline: (angle >= 10 && angle <= 170)
? 'top'
: (angle >= 190 && angle <= 350)
? 'bottom'
: 'middle',
textFont: textFont,
shadowColor: textStyle.shadowColor,
shadowBlur: textStyle.shadowBlur,
shadowOffsetX: textStyle.shadowOffsetX,
shadowOffsetY: textStyle.shadowOffsetY
}
}));
}
},
_buildPointer: function (seriesIndex) {
var serie = this.series[seriesIndex];
if (!serie.pointer.show) {
return;
}
var total = serie.max - serie.min;
var pointer = serie.pointer;
var params = this._paramsMap[seriesIndex];
var length = this.parsePercent(pointer.length, params.radius[1]);
var width = this.parsePercent(pointer.width, params.radius[1]);
var center = params.center;
var value = this._getValue(seriesIndex);
value = value < serie.max ? value : serie.max;
var angle = (params.startAngle - params.totalAngle / total * (value - serie.min))
* Math.PI / 180;
var color = pointer.color === 'auto'
? this._getColor(seriesIndex, value)
: pointer.color;
var pointShape = new GaugePointerShape({
zlevel: this.getZlevelBase(),
z: this.getZBase() + 1,
clickable: this.query(serie, 'clickable'),
style: {
x: center[0],
y: center[1],
r: length,
startAngle: params.startAngle * Math.PI / 180,
angle: angle,
color: color,
width: width,
shadowColor: pointer.shadowColor,
shadowBlur: pointer.shadowBlur,
shadowOffsetX: pointer.shadowOffsetX,
shadowOffsetY: pointer.shadowOffsetY
},
highlightStyle: {
brushType: 'fill',
width: width > 2 ? 2 : (width / 2),
color: '#fff'
}
});
ecData.pack(
pointShape,
this.series[seriesIndex], seriesIndex,
this.series[seriesIndex].data[0], 0,
this.series[seriesIndex].data[0].name,
value
);
this.shapeList.push(pointShape);
this.shapeList.push(new CircleShape({
zlevel: this.getZlevelBase(),
z: this.getZBase() + 2,
hoverable: false,
style: {
x: center[0],
y: center[1],
r: pointer.width / 2.5,
color: '#fff'
}
}));
},
_buildTitle: function(seriesIndex) {
var serie = this.series[seriesIndex];
if (!serie.title.show) {
return;
}
var data = serie.data[0];
var name = data.name != null ? data.name : '';
if (name !== '') { // 不要帮我代码规范
var title = serie.title;
var offsetCenter = title.offsetCenter;
var textStyle = title.textStyle;
var textColor = textStyle.color;
var params = this._paramsMap[seriesIndex];
var x = params.center[0] + this.parsePercent(offsetCenter[0], params.radius[1]);
var y = params.center[1] + this.parsePercent(offsetCenter[1], params.radius[1]);
this.shapeList.push(new TextShape({
zlevel: this.getZlevelBase(),
z: this.getZBase() + (
(Math.abs(x - params.center[0]) + Math.abs(y - params.center[1]))
< textStyle.fontSize * 2 ? 2 : 1
),
hoverable: false,
style: {
x: x,
y: y,
color: textColor === 'auto' ? this._getColor(seriesIndex) : textColor,
text: name,
textAlign: 'center',
textFont: this.getFont(textStyle),
shadowColor: textStyle.shadowColor,
shadowBlur: textStyle.shadowBlur,
shadowOffsetX: textStyle.shadowOffsetX,
shadowOffsetY: textStyle.shadowOffsetY
}
}));
}
},
_buildDetail: function(seriesIndex) {
var serie = this.series[seriesIndex];
if (!serie.detail.show) {
return;
}
var detail = serie.detail;
var offsetCenter = detail.offsetCenter;
var color = detail.backgroundColor;
var textStyle = detail.textStyle;
var textColor = textStyle.color;
var params = this._paramsMap[seriesIndex];
var value = this._getValue(seriesIndex);
var x = params.center[0] - detail.width / 2
+ this.parsePercent(offsetCenter[0], params.radius[1]);
var y = params.center[1]
+ this.parsePercent(offsetCenter[1], params.radius[1]);
this.shapeList.push(new RectangleShape({
zlevel: this.getZlevelBase(),
z: this.getZBase() + (
(Math.abs(x + detail.width / 2 - params.center[0])
+ Math.abs(y + detail.height / 2 - params.center[1])) < textStyle.fontSize
? 2 : 1
),
hoverable: false,
style: {
x: x,
y: y,
width: detail.width,
height: detail.height,
brushType: 'both',
color: color === 'auto' ? this._getColor(seriesIndex, value) : color,
lineWidth: detail.borderWidth,
strokeColor: detail.borderColor,
shadowColor: detail.shadowColor,
shadowBlur: detail.shadowBlur,
shadowOffsetX: detail.shadowOffsetX,
shadowOffsetY: detail.shadowOffsetY,
text: this._getLabelText(detail.formatter, value),
textFont: this.getFont(textStyle),
textPosition: 'inside',
textColor: textColor === 'auto' ? this._getColor(seriesIndex, value) : textColor
}
}));
},
_getValue: function(seriesIndex) {
return this.getDataFromOption(this.series[seriesIndex].data[0]);
},
/**
* 颜色索引
*/
_colorMap: function (seriesIndex) {
var serie = this.series[seriesIndex];
var min = serie.min;
var total = serie.max - min;
var color = serie.axisLine.lineStyle.color;
if (!(color instanceof Array)) {
color = [[1, color]];
}
var colorArray = [];
for (var i = 0, l = color.length; i < l; i++) {
colorArray.push([color[i][0] * total + min, color[i][1]]);
}
this._paramsMap[seriesIndex].colorArray = colorArray;
},
/**
* 自动颜色
*/
_getColor: function (seriesIndex, value) {
if (value == null) {
value = this._getValue(seriesIndex);
}
var colorArray = this._paramsMap[seriesIndex].colorArray;
for (var i = 0, l = colorArray.length; i < l; i++) {
if (colorArray[i][0] >= value) {
return colorArray[i][1];
}
}
return colorArray[colorArray.length - 1][1];
},
/**
* 构建扇形
*/
_getSector: function (center, r0, r, startAngle, endAngle, color, lineStyle) {
return new SectorShape ({
zlevel: this.getZlevelBase(),
z: this.getZBase(),
hoverable: false,
style: {
x: center[0], // 圆心横坐标
y: center[1], // 圆心纵坐标
r0: r0, // 圆环内半径
r: r, // 圆环外半径
startAngle: startAngle,
endAngle: endAngle,
brushType: 'fill',
color: color,
shadowColor: lineStyle.shadowColor,
shadowBlur: lineStyle.shadowBlur,
shadowOffsetX: lineStyle.shadowOffsetX,
shadowOffsetY: lineStyle.shadowOffsetY
}
});
},
/**
* 根据lable.format计算label text
*/
_getLabelText: function (formatter, value) {
if (formatter) {
if (typeof formatter === 'function') {
return formatter.call(this.myChart, value);
}
else if (typeof formatter === 'string') {
return formatter.replace('{value}', value);
}
}
return value;
},
/**
* 刷新
*/
refresh: function (newOption) {
if (newOption) {
this.option = newOption;
this.series = newOption.series;
}
this.backupShapeList();
this._buildShape();
}
};
zrUtil.inherits(Gauge, ChartBase);
// 图表注册
require('../chart').define('gauge', Gauge);
return Gauge;
});