blob: 8a0e0229985bf00c7a9f1bbc861584e37dc12303 [file] [log] [blame]
/**
* echarts组件类: 坐标轴
*
* @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
* @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
*
* 直角坐标系中坐标轴数组,数组中每一项代表一条横轴(纵轴)坐标轴。
* 标准(1.0)中规定最多同时存在2条横轴和2条纵轴
* 单条横轴时可指定安放于grid的底部(默认)或顶部,2条同时存在时则默认第一条安放于底部,第二天安放于顶部
* 单条纵轴时可指定安放于grid的左侧(默认)或右侧,2条同时存在时则默认第一条安放于左侧,第二天安放于右侧。
* 坐标轴有两种类型,类目型和数值型(区别详见axis):
* 横轴通常为类目型,但条形图时则横轴为数值型,散点图时则横纵均为数值型
* 纵轴通常为数值型,但条形图时则纵轴为类目型。
*
*/
define(function (require) {
var Base = require('./base');
var LineShape = require('zrender/shape/Line');
var ecConfig = require('../config');
var ecData = require('../util/ecData');
var zrUtil = require('zrender/tool/util');
var zrColor = require('zrender/tool/color');
/**
* 构造函数
* @param {Object} messageCenter echart消息中心
* @param {ZRender} zr zrender实例
* @param {Object} option 图表选项
* @param {string=} option.xAxis.type 坐标轴类型,横轴默认为类目型'category'
* @param {string=} option.yAxis.type 坐标轴类型,纵轴默认为类目型'value'
* @param {Object} component 组件
* @param {string} axisType 横走or纵轴
*/
function Axis(ecTheme, messageCenter, zr, option, myChart, axisType) {
Base.call(this, ecTheme, messageCenter, zr, option, myChart);
this.axisType = axisType;
this._axisList = [];
this.refresh(option);
}
Axis.prototype = {
type: ecConfig.COMPONENT_TYPE_AXIS,
axisBase: {
// 轴线
_buildAxisLine: function () {
var lineWidth = this.option.axisLine.lineStyle.width;
var halfLineWidth = lineWidth / 2;
var axShape = {
_axisShape: 'axisLine',
zlevel: this.getZlevelBase(),
z: this.getZBase() + 3,
hoverable: false
};
var grid = this.grid;
switch (this.option.position) {
case 'left' :
axShape.style = {
xStart: grid.getX() - halfLineWidth,
yStart: grid.getYend(),
xEnd: grid.getX() - halfLineWidth,
yEnd: grid.getY(),
lineCap: 'round'
};
break;
case 'right' :
axShape.style = {
xStart: grid.getXend() + halfLineWidth,
yStart: grid.getYend(),
xEnd: grid.getXend() + halfLineWidth,
yEnd: grid.getY(),
lineCap: 'round'
};
break;
case 'bottom' :
axShape.style = {
xStart: grid.getX(),
yStart: grid.getYend() + halfLineWidth,
xEnd: grid.getXend(),
yEnd: grid.getYend() + halfLineWidth,
lineCap: 'round'
};
break;
case 'top' :
axShape.style = {
xStart: grid.getX(),
yStart: grid.getY() - halfLineWidth,
xEnd: grid.getXend(),
yEnd: grid.getY() - halfLineWidth,
lineCap: 'round'
};
break;
}
var style = axShape.style;
if (this.option.name !== '') { // 别帮我代码规范
style.text = this.option.name;
style.textPosition = this.option.nameLocation;
style.textFont = this.getFont(this.option.nameTextStyle);
if (this.option.nameTextStyle.align) {
style.textAlign = this.option.nameTextStyle.align;
}
if (this.option.nameTextStyle.baseline) {
style.textBaseline = this.option.nameTextStyle.baseline;
}
if (this.option.nameTextStyle.color) {
style.textColor = this.option.nameTextStyle.color;
}
}
style.strokeColor = this.option.axisLine.lineStyle.color;
style.lineWidth = lineWidth;
// 亚像素优化
if (this.isHorizontal()) {
// 横向布局,优化y
style.yStart
= style.yEnd
= this.subPixelOptimize(style.yEnd, lineWidth);
}
else {
// 纵向布局,优化x
style.xStart
= style.xEnd
= this.subPixelOptimize(style.xEnd, lineWidth);
}
style.lineType = this.option.axisLine.lineStyle.type;
axShape = new LineShape(axShape);
this.shapeList.push(axShape);
},
_axisLabelClickable: function(clickable, axShape) {
if (clickable) {
ecData.pack(
axShape, undefined, -1, undefined, -1, axShape.style.text
);
axShape.hoverable = true;
axShape.clickable = true;
axShape.highlightStyle = {
color: zrColor.lift(axShape.style.color, 1),
brushType: 'fill'
};
return axShape;
}
else {
return axShape;
}
},
refixAxisShape: function(zeroX, zeroY) {
if (!this.option.axisLine.onZero) {
return;
}
var tickLength;
if (this.isHorizontal() && zeroY != null) {
// 横向布局调整纵向y
for (var i = 0, l = this.shapeList.length; i < l; i++) {
if (this.shapeList[i]._axisShape === 'axisLine') {
this.shapeList[i].style.yStart
= this.shapeList[i].style.yEnd
= this.subPixelOptimize(
zeroY, this.shapeList[i].stylelineWidth
);
this.zr.modShape(this.shapeList[i].id);
}
else if (this.shapeList[i]._axisShape === 'axisTick') {
tickLength = this.shapeList[i].style.yEnd
- this.shapeList[i].style.yStart;
this.shapeList[i].style.yStart = zeroY - tickLength;
this.shapeList[i].style.yEnd = zeroY;
this.zr.modShape(this.shapeList[i].id);
}
}
}
if (!this.isHorizontal() && zeroX != null) {
// 纵向布局调整横向x
for (var i = 0, l = this.shapeList.length; i < l; i++) {
if (this.shapeList[i]._axisShape === 'axisLine') {
this.shapeList[i].style.xStart
= this.shapeList[i].style.xEnd
= this.subPixelOptimize(
zeroX, this.shapeList[i].stylelineWidth
);
this.zr.modShape(this.shapeList[i].id);
}
else if (this.shapeList[i]._axisShape === 'axisTick') {
tickLength = this.shapeList[i].style.xEnd
- this.shapeList[i].style.xStart;
this.shapeList[i].style.xStart = zeroX;
this.shapeList[i].style.xEnd = zeroX + tickLength;
this.zr.modShape(this.shapeList[i].id);
}
}
}
},
getPosition: function () {
return this.option.position;
},
isHorizontal: function() {
return this.option.position === 'bottom' || this.option.position === 'top';
}
},
/**
* 参数修正&默认值赋值,重载基类方法
* @param {Object} opt 参数
*/
reformOption: function (opt) {
// 不写或传了个空数值默认为数值轴
if (!opt || (opt instanceof Array && opt.length === 0)) {
opt = [ { type: ecConfig.COMPONENT_TYPE_AXIS_VALUE } ];
}
else if (!(opt instanceof Array)){
opt = [opt];
}
// 最多两条,其他参数忽略
if (opt.length > 2) {
opt = [opt[0], opt[1]];
}
if (this.axisType === 'xAxis') {
// 横轴位置默认配置
if (!opt[0].position // 没配置或配置错
|| (opt[0].position != 'bottom' && opt[0].position != 'top')
) {
opt[0].position = 'bottom';
}
if (opt.length > 1) {
opt[1].position = opt[0].position === 'bottom' ? 'top' : 'bottom';
}
for (var i = 0, l = opt.length; i < l; i++) {
// 坐标轴类型,横轴默认为类目型'category'
opt[i].type = opt[i].type || 'category';
// 标识轴类型&索引
opt[i].xAxisIndex = i;
opt[i].yAxisIndex = -1;
}
}
else {
// 纵轴位置默认配置
if (!opt[0].position // 没配置或配置错
|| (opt[0].position != 'left' && opt[0].position != 'right')
) {
opt[0].position = 'left';
}
if (opt.length > 1) {
opt[1].position = opt[0].position === 'left' ? 'right' : 'left';
}
for (var i = 0, l = opt.length; i < l; i++) {
// 坐标轴类型,纵轴默认为数值型'value'
opt[i].type = opt[i].type || 'value';
// 标识轴类型&索引
opt[i].xAxisIndex = -1;
opt[i].yAxisIndex = i;
}
}
return opt;
},
/**
* 刷新
*/
refresh: function (newOption) {
var axisOption;
if (newOption) {
this.option = newOption;
if (this.axisType === 'xAxis') {
this.option.xAxis = this.reformOption(newOption.xAxis);
axisOption = this.option.xAxis;
}
else {
this.option.yAxis = this.reformOption(newOption.yAxis);
axisOption = this.option.yAxis;
}
this.series = newOption.series;
}
var CategoryAxis = require('./categoryAxis');
var ValueAxis = require('./valueAxis');
var len = Math.max((axisOption && axisOption.length || 0), this._axisList.length);
for (var i = 0; i < len; i++) {
if (this._axisList[i] // 已有实例
&& newOption // 非空刷新
&& (!axisOption[i] || this._axisList[i].type != axisOption[i].type) // 类型不匹配
) {
this._axisList[i].dispose && this._axisList[i].dispose();
this._axisList[i] = false;
}
if (this._axisList[i]) {
this._axisList[i].refresh && this._axisList[i].refresh(
axisOption ? axisOption[i] : false,
this.series
);
}
else if (axisOption && axisOption[i]) {
this._axisList[i] = axisOption[i].type === 'category'
? new CategoryAxis(
this.ecTheme, this.messageCenter, this.zr,
axisOption[i], this.myChart, this.axisBase
)
: new ValueAxis(
this.ecTheme, this.messageCenter, this.zr,
axisOption[i], this.myChart, this.axisBase,
this.series
);
}
}
},
/**
* 根据值换算位置
* @param {number} idx 坐标轴索引0~1
*/
getAxis: function (idx) {
return this._axisList[idx];
},
clear: function () {
for (var i = 0, l = this._axisList.length; i < l; i++) {
this._axisList[i].dispose && this._axisList[i].dispose();
}
this._axisList = [];
}
};
zrUtil.inherits(Axis, Base);
require('../component').define('axis', Axis);
return Axis;
});