blob: 9d5a40789c7d43cbb01931285e43f3b4a38791d9 [file] [log] [blame]
/**
* echarts组件:数据区域缩放
* Copyright 2013 Baidu Inc. All rights reserved.
*
* @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
* @author Kener (@Kener-林峰, linzhifeng@baidu.com)
*
*/
define(function (require) {
/**
* 构造函数
* @param {Object} messageCenter echart消息中心
* @param {ZRender} zr zrender实例
* @param {Object} option 图表参数
* @param {Object} component 组件
*/
function DataZoom(messageCenter, zr, option, component) {
var Base = require('./base');
Base.call(this, zr);
var ecConfig = require('../config');
var self = this;
self.type = ecConfig.COMPONENT_TYPE_DATAZOOM;
var _zlevelBase = self.getZlevelBase();
var zoomOption;
var _fillerSize = 30; // 填充大小
var _handleSize = 10; // 手柄大小
var _location; // 位置参数,通过计算所得x, y, width, height
var _zoom; // 缩放参数
var _fillerShae;
var _startShape;
var _endShape;
var _syncTicket;
var _originalData;
function _buildShape() {
_location = _getLocation();
_zoom = _getZoom();
_backupData();
_buildBackground();
_buildDataBackground();
_buildFiller();
_bulidHandle();
for (var i = 0, l = self.shapeList.length; i < l; i++) {
self.shapeList[i].id = zr.newShapeId(self.type);
zr.addShape(self.shapeList[i]);
}
_syncData();
}
/**
* 根据选项计算实体的位置坐标
*/
function _getLocation() {
var x;
var y;
var width;
var height;
var grid = component.grid;
// 不指定则根据grid适配
if (zoomOption.orient == 'horizontal') {
// 水平布局
width = zoomOption.width || grid.getWidth();
height = zoomOption.height || _fillerSize;
x = typeof zoomOption.x != 'undefined'
? zoomOption.x : grid.getX();
y = typeof zoomOption.y != 'undefined'
? zoomOption.y : (zr.getHeight() - height);
}
else {
// 垂直布局
width = zoomOption.width || _fillerSize;
height = zoomOption.height || grid.getHeight();
x = typeof zoomOption.x != 'undefined'
? zoomOption.x : 0;
y = typeof zoomOption.y != 'undefined'
? zoomOption.y : grid.getY();
}
return {
x : x,
y : y,
width : width,
height : height
};
}
/**
* 计算缩放参数
* 修正单坐标轴只传对象为数组。
*/
function _getZoom() {
var series = option.series;
var xAxis = option.xAxis;
if (xAxis && !(xAxis instanceof Array)) {
xAxis = [xAxis];
option.xAxis = xAxis;
}
var yAxis = option.yAxis;
if (yAxis && !(yAxis instanceof Array)) {
yAxis = [yAxis];
option.yAxis = yAxis;
}
var zoomSeriesIndex = [];
var xAxisIndex;
var yAxisIndex;
var zOptIdx = zoomOption.xAxisIndex;
if (xAxis && typeof zOptIdx == 'undefined') {
xAxisIndex = [];
for (var i = 0, l = xAxis.length; i < l; i++) {
// 横纵默认为类目轴
if (xAxis[i].type == 'category'
|| typeof xAxis[i].type == 'undefined'
) {
xAxisIndex.push(i);
}
}
}
else {
if (zOptIdx instanceof Array) {
xAxisIndex = zOptIdx;
}
else if (typeof zOptIdx != 'undefined') {
xAxisIndex = [zOptIdx];
}
else {
xAxisIndex = [];
}
}
zOptIdx = zoomOption.yAxisIndex;
if (yAxis && typeof zOptIdx == 'undefined') {
yAxisIndex = [];
for (var i = 0, l = yAxis.length; i < l; i++) {
if (yAxis[i].type == 'category') {
yAxisIndex.push(i);
}
}
}
else {
if (zOptIdx instanceof Array) {
yAxisIndex = zOptIdx;
}
else if (typeof zOptIdx != 'undefined') {
yAxisIndex = [zOptIdx];
}
else {
yAxisIndex = [];
}
}
// 找到缩放控制的所有series
for (var i = 0, l = series.length; i < l; i++) {
for (var j = 0, k = xAxisIndex.length; j < k; j++) {
if (xAxisIndex[j] == (series[i].xAxisIndex || 0)) {
zoomSeriesIndex.push(i);
break;
}
}
for (var j = 0, k = yAxisIndex.length; j < k; j++) {
if (yAxisIndex[j] == (series[i].yAxisIndex || 0)) {
zoomSeriesIndex.push(i);
break;
}
}
}
var start = typeof zoomOption.start != 'undefined'
&& zoomOption.start >= 0
&& zoomOption.start <= 100
? zoomOption.start : 0;
var end = typeof zoomOption.end != 'undefined'
&& zoomOption.end >= 0
&& zoomOption.end <= 100
? zoomOption.end : 100;
if (start > end) {
// 大小颠倒自动翻转
start = start + end;
end = start - end;
start = start - end;
}
var size = Math.round(
(end - start) / 100
* (zoomOption.orient == 'horizontal'
? _location.width : _location.height)
);
return {
start : start,
end : end,
size : size,
xAxisIndex : xAxisIndex,
yAxisIndex : yAxisIndex,
seriesIndex : zoomSeriesIndex
};
}
function _backupData() {
_originalData = {
xAxis : {},
yAxis : {},
series : {}
};
var xAxis = option.xAxis;
var xAxisIndex = _zoom.xAxisIndex;
for (var i = 0, l = xAxisIndex.length; i < l; i++) {
_originalData.xAxis[xAxisIndex[i]] = xAxis[xAxisIndex[i]].data;
}
var yAxis = option.yAxis;
var yAxisIndex = _zoom.yAxisIndex;
for (var i = 0, l = yAxisIndex.length; i < l; i++) {
_originalData.yAxis[yAxisIndex[i]] = yAxis[yAxisIndex[i]].data;
}
var series = option.series;
var seriesIndex = _zoom.seriesIndex;
for (var i = 0, l = seriesIndex.length; i < l; i++) {
_originalData.series[seriesIndex[i]]
= series[seriesIndex[i]].data;
}
}
function _buildBackground() {
self.shapeList.push({
shape : 'rectangle',
zlevel : _zlevelBase,
hoverable :false,
style : {
x : _location.x,
y : _location.y,
width : _location.width,
height : _location.height,
color : zoomOption.backgroundColor
}
});
}
function _buildDataBackground() {
self.shapeList.push({
shape : 'rectangle',
zlevel : _zlevelBase,
hoverable :false,
style : {
x : _location.x,
y : _location.y,
width : _location.width,
height : _location.height,
color : zoomOption.backgroundColor
}
});
var maxLength = 0;
var xAxis = option.xAxis;
var xAxisIndex = _zoom.xAxisIndex;
for (var i = 0, l = xAxisIndex.length; i < l; i++) {
maxLength = Math.max(
maxLength, xAxis[xAxisIndex[i]].data.length
);
}
var yAxis = option.yAxis;
var yAxisIndex = _zoom.yAxisIndex;
for (var i = 0, l = yAxisIndex.length; i < l; i++) {
maxLength = Math.max(
maxLength, yAxis[yAxisIndex[i]].data.length
);
}
var data = option.series[_zoom.seriesIndex[0]].data;
var maxValue = Number.MIN_VALUE;
var minValue = Number.MAX_VALUE;
var value;
for (var i = 0, l = data.length; i < l; i++) {
value = typeof data[i] != 'undefined'
? (typeof data[i].value != 'undefined'
? data[i].value : data[i])
: 0;
if (isNaN(value)) {
value = 0;
}
maxValue = Math.max(maxValue, value);
minValue = Math.min(minValue, value);
}
var pointList = [];
var x = _location.width / maxLength;
var y = _location.height / maxLength;
for (var i = 0, l = maxLength; i < l; i++) {
value = typeof data[i] != 'undefined'
? (typeof data[i].value != 'undefined'
? data[i].value : data[i])
: 0;
if (isNaN(value)) {
value = 0;
}
if (zoomOption.orient == 'horizontal') {
pointList.push([
_location.x + x * i,
_location.y + _location.height - 5 - Math.round(
(value - minValue)
/ (maxValue - minValue)
* (_location.height - 10)
)
]);
}
else {
pointList.push([
_location.x + 5 + Math.round(
(value - minValue)
/ (maxValue - minValue)
* (_location.width - 10)
),
_location.y + y * i
]);
}
}
if (zoomOption.orient == 'horizontal') {
pointList.push([
_location.x + _location.width,
_location.y + _location.height
]);
pointList.push([
_location.x, _location.y + _location.height
]);
}
else {
pointList.push([
_location.x, _location.y + _location.height
]);
pointList.push([
_location.x, _location.y
]);
}
self.shapeList.push({
shape : 'polygon',
zlevel : _zlevelBase,
style : {
pointList : pointList,
color : zoomOption.dataBackgroundColor
},
hoverable : false
});
}
/**
* 构建填充物
*/
function _buildFiller() {
_fillerShae = {
shape : 'rectangle',
zlevel : _zlevelBase,
draggable : true,
ondrift : _ondrift,
ondragend : _ondragend,
_type : 'filler'
};
if (zoomOption.orient == 'horizontal') {
// 横向
_fillerShae.style = {
x : _location.x
+ Math.round(_zoom.start / 100 * _location.width)
+ _handleSize,
y : _location.y + 3,
width : _zoom.size - _handleSize * 2,
height : _location.height - 6,
color : zoomOption.fillerColor,
text : ':::',
textPosition : 'inside'
};
}
else {
_fillerShae.style ={
x : _location.x + 3,
y : _location.y
+ Math.round(_zoom.start / 100 * _location.height)
+ _handleSize,
width : _location.width - 6,
height : _zoom.size - _handleSize * 2,
color : zoomOption.fillerColor,
text : '=',
textPosition : 'inside'
};
}
self.shapeList.push(_fillerShae);
}
/**
* 构建拖拽手柄
*/
function _bulidHandle() {
_startShape = {
shape : 'rectangle',
zlevel : _zlevelBase
};
_endShape = {
shape : 'rectangle',
zlevel : _zlevelBase
};
if (!zoomOption.zoomLock) {
_startShape.draggable = true;
_startShape.ondrift = _ondrift;
_startShape.ondragend = _ondragend;
_endShape.draggable = true;
_endShape.ondrift = _ondrift;
_endShape.ondragend = _ondragend;
}
if (zoomOption.orient == 'horizontal') {
// 头
_startShape.style = {
x : _fillerShae.style.x - _handleSize,
y : _location.y,
width : _handleSize,
height : _location.height,
color : zoomOption.handleColor,
text : '|',
textPosition : 'inside'
};
// 尾
_endShape.style = {
x : _fillerShae.style.x + _fillerShae.style.width,
y : _location.y,
width : _handleSize,
height : _location.height,
color : zoomOption.handleColor,
text : '|',
textPosition : 'inside'
};
}
else {
// 头
_startShape.style = {
x : _location.x,
y : _fillerShae.style.y - _handleSize,
width : _location.width,
height : _handleSize,
color : zoomOption.handleColor,
text : '—',
textPosition : 'inside'
};
// 尾
_endShape.style = {
x : _location.x,
y : _fillerShae.style.y + _fillerShae.style.height,
width : _location.width,
height : _handleSize,
color : zoomOption.handleColor,
text : '—',
textPosition : 'inside'
};
}
self.shapeList.push(_startShape);
self.shapeList.push(_endShape);
}
/**
* 拖拽范围控制
*/
function _ondrift(e, dx, dy) {
var detailSize = e._type == 'filler' ? _handleSize : 0;
if (zoomOption.orient == 'horizontal') {
if (e.style.x + dx - detailSize <= _location.x) {
e.style.x = _location.x + detailSize;
}
else if (e.style.x + dx + e.style.width + detailSize
>= _location.x + _location.width
) {
e.style.x = _location.x + _location.width
- e.style.width - detailSize;
}
else {
e.style.x += dx;
}
}
else {
if (e.style.y + dy - detailSize <= _location.y) {
e.style.y = _location.y + detailSize;
}
else if (e.style.y + dy + e.style.height + detailSize
>= _location.y + _location.height
) {
e.style.y = _location.y + _location.height
- e.style.height - detailSize;
}
else {
e.style.y += dy;
}
}
if (e._type == 'filler') {
_syncHandleShape();
}
else {
_syncFillerShape();
}
if (zoomOption.realtime) {
_syncData();
}
else {
clearTimeout(_syncTicket);
_syncTicket = setTimeout(_syncData, 200);
}
return true;
}
function _syncHandleShape() {
if (zoomOption.orient == 'horizontal') {
_startShape.style.x = _fillerShae.style.x - _handleSize;
_endShape.style.x = _fillerShae.style.x
+ _fillerShae.style.width;
_zoom.start = Math.floor(
(_startShape.style.x - _location.x)
/ _location.width * 100
);
_zoom.end = Math.ceil(
(_endShape.style.x + _handleSize - _location.x)
/ _location.width * 100
);
}
else {
_startShape.style.y = _fillerShae.style.y - _handleSize;
_endShape.style.y = _fillerShae.style.y
+ _fillerShae.style.height;
_zoom.start = Math.floor(
(_startShape.style.y - _location.y)
/ _location.height * 100
);
_zoom.end = Math.ceil(
(_endShape.style.y + _handleSize - _location.y)
/ _location.height * 100
);
}
zr.modShape(_startShape.id, _startShape);
zr.modShape(_endShape.id, _endShape);
zr.refresh();
}
function _syncFillerShape() {
var a;
var b;
if (zoomOption.orient == 'horizontal') {
a = _startShape.style.x;
b = _endShape.style.x;
_fillerShae.style.x = Math.min(a, b) + _handleSize;
_fillerShae.style.width = Math.abs(a - b) - _handleSize;
_zoom.start = Math.floor(
(Math.min(a, b) - _location.x)
/ _location.width * 100
);
_zoom.end = Math.ceil(
(Math.max(a, b) + _handleSize - _location.x)
/ _location.width * 100
);
}
else {
a = _startShape.style.y;
b = _endShape.style.y;
_fillerShae.style.y = Math.min(a, b) + _handleSize;
_fillerShae.style.height = Math.abs(a - b) - _handleSize;
_zoom.start = Math.floor(
(Math.min(a, b) - _location.y)
/ _location.height * 100
);
_zoom.end = Math.ceil(
(Math.max(a, b) + _handleSize - _location.y)
/ _location.height * 100
);
}
zr.modShape(_fillerShae.id, _fillerShae);
zr.refresh();
}
function _syncData() {
var target;
var start;
var end;
var length;
var data;
for (var key in _originalData) {
target = _originalData[key];
for (var idx in target) {
data = target[idx];
length = data.length;
start = Math.floor(_zoom.start / 100 * length);
end = Math.ceil(_zoom.end / 100 * length);
option[key][idx].data = data.slice(start, end);
}
}
if (zoomOption.realtime) {
messageCenter.dispatch(ecConfig.EVENT.DATA_ZOOM);
}
zoomOption.start = _zoom.start;
zoomOption.end = _zoom.end;
}
function _ondragend() {
self.isDragend = true;
}
/**
* 数据项被拖拽出去
*/
function ondragend(param, status) {
if (!self.isDragend || !param.target) {
// 没有在当前实例上发生拖拽行为则直接返回
return;
}
_syncData();
// 别status = {}赋值啊!!
status.dragOut = true;
status.dragIn = true;
if (!zoomOption.realtime) {
messageCenter.dispatch(ecConfig.EVENT.DATA_ZOOM);
}
status.needRefresh = false; // 会有消息触发fresh,不用再刷一遍
// 处理完拖拽事件后复位
self.isDragend = false;
return;
}
function ondataZoom(param, status) {
status.needRefresh = true;
return;
}
function init(newOption) {
option = newOption;
option.dataZoom = self.reformOption(option.dataZoom);
zoomOption = option.dataZoom;
self.clear();
if (option.dataZoom.show) {
_buildShape();
}
}
self.init = init;
self.ondragend = ondragend;
self.ondataZoom = ondataZoom;
init(option);
}
return DataZoom;
});