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.
1759 lines
63 KiB
1759 lines
63 KiB
/*!
|
|
* ECharts, a javascript interactive chart library.
|
|
*
|
|
* Copyright (c) 2015, Baidu Inc.
|
|
* All rights reserved.
|
|
*
|
|
* LICENSE
|
|
* https://github.com/ecomfe/echarts/blob/master/LICENSE.txt
|
|
*/
|
|
|
|
/**
|
|
* echarts
|
|
*
|
|
* @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
|
|
* @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
|
|
*
|
|
*/
|
|
define(function (require) {
|
|
var ecConfig = require('./config');
|
|
var zrUtil = require('zrender/tool/util');
|
|
var zrEvent = require('zrender/tool/event');
|
|
|
|
var self = {};
|
|
|
|
var _canvasSupported = require('zrender/tool/env').canvasSupported;
|
|
var _idBase = new Date() - 0;
|
|
var _instances = {}; // ECharts实例map索引
|
|
var DOM_ATTRIBUTE_KEY = '_echarts_instance_';
|
|
|
|
self.version = '2.2.7';
|
|
self.dependencies = {
|
|
zrender: '2.1.1'
|
|
};
|
|
/**
|
|
* 入口方法
|
|
*/
|
|
self.init = function (dom, theme) {
|
|
var zrender = require('zrender');
|
|
if ((zrender.version.replace('.', '') - 0) < (self.dependencies.zrender.replace('.', '') - 0)) {
|
|
console.error(
|
|
'ZRender ' + zrender.version
|
|
+ ' is too old for ECharts ' + self.version
|
|
+ '. Current version need ZRender '
|
|
+ self.dependencies.zrender + '+'
|
|
);
|
|
}
|
|
|
|
dom = dom instanceof Array ? dom[0] : dom;
|
|
|
|
// dom与echarts实例映射索引
|
|
var key = dom.getAttribute(DOM_ATTRIBUTE_KEY);
|
|
if (!key) {
|
|
key = _idBase++;
|
|
dom.setAttribute(DOM_ATTRIBUTE_KEY, key);
|
|
}
|
|
|
|
if (_instances[key]) {
|
|
// 同一个dom上多次init,自动释放已有实例
|
|
_instances[key].dispose();
|
|
}
|
|
_instances[key] = new Echarts(dom);
|
|
_instances[key].id = key;
|
|
_instances[key].canvasSupported = _canvasSupported;
|
|
_instances[key].setTheme(theme);
|
|
|
|
return _instances[key];
|
|
};
|
|
|
|
/**
|
|
* 通过id获得ECharts实例,id可在实例化后读取
|
|
*/
|
|
self.getInstanceById = function (key) {
|
|
return _instances[key];
|
|
};
|
|
|
|
/**
|
|
* 消息中心
|
|
*/
|
|
function MessageCenter() {
|
|
zrEvent.Dispatcher.call(this);
|
|
}
|
|
zrUtil.merge(MessageCenter.prototype, zrEvent.Dispatcher.prototype, true);
|
|
|
|
/**
|
|
* 基于zrender实现Echarts接口层
|
|
* @param {HtmlElement} dom 必要
|
|
*/
|
|
function Echarts(dom) {
|
|
// Fxxk IE11 for breaking initialization without a warrant;
|
|
// Just set something to let it be!
|
|
// by kener 2015-01-09
|
|
dom.innerHTML = '';
|
|
this._themeConfig = {}; // zrUtil.clone(ecConfig);
|
|
|
|
this.dom = dom;
|
|
// this._zr;
|
|
// this._option; // curOption clone
|
|
// this._optionRestore; // for restore;
|
|
// this._island;
|
|
// this._toolbox;
|
|
// this._timeline;
|
|
// this._refreshInside; // 内部刷新标志位
|
|
|
|
this._connected = false;
|
|
this._status = { // 用于图表间通信
|
|
dragIn: false,
|
|
dragOut: false,
|
|
needRefresh: false
|
|
};
|
|
this._curEventType = false; // 破循环信号灯
|
|
this._chartList = []; // 图表实例
|
|
|
|
this._messageCenter = new MessageCenter();
|
|
|
|
this._messageCenterOutSide = new MessageCenter(); // Echarts层的外部消息中心,做Echarts层的消息转发
|
|
|
|
// resize方法经常被绑定到window.resize上,闭包一个this
|
|
this.resize = this.resize();
|
|
|
|
// 初始化::构造函数
|
|
this._init();
|
|
}
|
|
|
|
/**
|
|
* ZRender EVENT
|
|
*
|
|
* @inner
|
|
* @const
|
|
* @type {Object}
|
|
*/
|
|
var ZR_EVENT = require('zrender/config').EVENT;
|
|
|
|
/**
|
|
* 要绑定监听的zrender事件列表
|
|
*
|
|
* @const
|
|
* @inner
|
|
* @type {Array}
|
|
*/
|
|
var ZR_EVENT_LISTENS = [
|
|
'CLICK', 'DBLCLICK', 'MOUSEOVER', 'MOUSEOUT',
|
|
'DRAGSTART', 'DRAGEND', 'DRAGENTER', 'DRAGOVER', 'DRAGLEAVE', 'DROP'
|
|
];
|
|
|
|
/**
|
|
* 对echarts的实例中的chartList属性成员,逐个进行方法调用,遍历顺序为逆序
|
|
* 由于在事件触发的默认行为处理中,多次用到相同逻辑,所以抽象了该方法
|
|
* 由于所有的调用场景里,最多只有两个参数,基于性能和体积考虑,这里就不使用call或者apply了
|
|
*
|
|
* @inner
|
|
* @param {ECharts} ecInstance ECharts实例
|
|
* @param {string} methodName 要调用的方法名
|
|
* @param {*} arg0 调用参数1
|
|
* @param {*} arg1 调用参数2
|
|
* @param {*} arg2 调用参数3
|
|
*/
|
|
function callChartListMethodReverse(ecInstance, methodName, arg0, arg1, arg2) {
|
|
var chartList = ecInstance._chartList;
|
|
var len = chartList.length;
|
|
|
|
while (len--) {
|
|
var chart = chartList[len];
|
|
if (typeof chart[methodName] === 'function') {
|
|
chart[methodName](arg0, arg1, arg2);
|
|
}
|
|
}
|
|
}
|
|
|
|
Echarts.prototype = {
|
|
/**
|
|
* 初始化::构造函数
|
|
*/
|
|
_init: function () {
|
|
var self = this;
|
|
var _zr = require('zrender').init(this.dom);
|
|
this._zr = _zr;
|
|
|
|
// wrap: n,e,d,t for name event data this
|
|
this._messageCenter.dispatch = function(type, event, eventPackage, that) {
|
|
eventPackage = eventPackage || {};
|
|
eventPackage.type = type;
|
|
eventPackage.event = event;
|
|
|
|
self._messageCenter.dispatchWithContext(type, eventPackage, that);
|
|
self._messageCenterOutSide.dispatchWithContext(type, eventPackage, that);
|
|
|
|
// 如下注掉的代码,@see: https://github.com/ecomfe/echarts-discuss/issues/3
|
|
// if (type != 'HOVER' && type != 'MOUSEOUT') { // 频繁事件直接抛出
|
|
// setTimeout(function(){
|
|
// self._messageCenterOutSide.dispatchWithContext(
|
|
// type, eventPackage, that
|
|
// );
|
|
// },50);
|
|
// }
|
|
// else {
|
|
// self._messageCenterOutSide.dispatchWithContext(
|
|
// type, eventPackage, that
|
|
// );
|
|
// }
|
|
};
|
|
|
|
this._onevent = function(param){
|
|
return self.__onevent(param);
|
|
};
|
|
for (var e in ecConfig.EVENT) {
|
|
if (e != 'CLICK' && e != 'DBLCLICK'
|
|
&& e != 'HOVER' && e != 'MOUSEOUT' && e != 'MAP_ROAM'
|
|
) {
|
|
this._messageCenter.bind(ecConfig.EVENT[e], this._onevent, this);
|
|
}
|
|
}
|
|
|
|
|
|
var eventBehaviors = {};
|
|
this._onzrevent = function (param) {
|
|
return self[eventBehaviors[ param.type ]](param);
|
|
};
|
|
|
|
// 挂载关心的事件
|
|
for (var i = 0, len = ZR_EVENT_LISTENS.length; i < len; i++) {
|
|
var eventName = ZR_EVENT_LISTENS[i];
|
|
var eventValue = ZR_EVENT[eventName];
|
|
eventBehaviors[eventValue] = '_on' + eventName.toLowerCase();
|
|
_zr.on(eventValue, this._onzrevent);
|
|
}
|
|
|
|
this.chart = {}; // 图表索引
|
|
this.component = {}; // 组件索引
|
|
|
|
// 内置图表
|
|
// 孤岛
|
|
var Island = require('./chart/island');
|
|
this._island = new Island(this._themeConfig, this._messageCenter, _zr, {}, this);
|
|
this.chart.island = this._island;
|
|
|
|
// 内置通用组件
|
|
// 工具箱
|
|
var Toolbox = require('./component/toolbox');
|
|
this._toolbox = new Toolbox(this._themeConfig, this._messageCenter, _zr, {}, this);
|
|
this.component.toolbox = this._toolbox;
|
|
|
|
var componentLibrary = require('./component');
|
|
componentLibrary.define('title', require('./component/title'));
|
|
componentLibrary.define('tooltip', require('./component/tooltip'));
|
|
componentLibrary.define('legend', require('./component/legend'));
|
|
|
|
if (_zr.getWidth() === 0 || _zr.getHeight() === 0) {
|
|
console.error('Dom’s width & height should be ready before init.');
|
|
}
|
|
},
|
|
|
|
/**
|
|
* ECharts事件处理中心
|
|
*/
|
|
__onevent: function (param){
|
|
param.__echartsId = param.__echartsId || this.id;
|
|
|
|
// 来自其他联动图表的事件
|
|
var fromMyself = (param.__echartsId === this.id);
|
|
|
|
if (!this._curEventType) {
|
|
this._curEventType = param.type;
|
|
}
|
|
|
|
switch (param.type) {
|
|
case ecConfig.EVENT.LEGEND_SELECTED :
|
|
this._onlegendSelected(param);
|
|
break;
|
|
case ecConfig.EVENT.DATA_ZOOM :
|
|
if (!fromMyself) {
|
|
var dz = this.component.dataZoom;
|
|
if (dz) {
|
|
dz.silence(true);
|
|
dz.absoluteZoom(param.zoom);
|
|
dz.silence(false);
|
|
}
|
|
}
|
|
this._ondataZoom(param);
|
|
break;
|
|
case ecConfig.EVENT.DATA_RANGE :
|
|
fromMyself && this._ondataRange(param);
|
|
break;
|
|
case ecConfig.EVENT.MAGIC_TYPE_CHANGED :
|
|
if (!fromMyself) {
|
|
var tb = this.component.toolbox;
|
|
if (tb) {
|
|
tb.silence(true);
|
|
tb.setMagicType(param.magicType);
|
|
tb.silence(false);
|
|
}
|
|
}
|
|
this._onmagicTypeChanged(param);
|
|
break;
|
|
case ecConfig.EVENT.DATA_VIEW_CHANGED :
|
|
fromMyself && this._ondataViewChanged(param);
|
|
break;
|
|
case ecConfig.EVENT.TOOLTIP_HOVER :
|
|
fromMyself && this._tooltipHover(param);
|
|
break;
|
|
case ecConfig.EVENT.RESTORE :
|
|
this._onrestore();
|
|
break;
|
|
case ecConfig.EVENT.REFRESH :
|
|
fromMyself && this._onrefresh(param);
|
|
break;
|
|
// 鼠标同步
|
|
case ecConfig.EVENT.TOOLTIP_IN_GRID :
|
|
case ecConfig.EVENT.TOOLTIP_OUT_GRID :
|
|
if (!fromMyself) {
|
|
// 只处理来自外部的鼠标同步
|
|
var grid = this.component.grid;
|
|
if (grid) {
|
|
this._zr.trigger(
|
|
'mousemove',
|
|
{
|
|
connectTrigger: true,
|
|
zrenderX: grid.getX() + param.x * grid.getWidth(),
|
|
zrenderY: grid.getY() + param.y * grid.getHeight()
|
|
}
|
|
);
|
|
}
|
|
}
|
|
else if (this._connected) {
|
|
// 来自自己,并且存在多图联动,空间坐标映射修改参数分发
|
|
var grid = this.component.grid;
|
|
if (grid) {
|
|
param.x = (param.event.zrenderX - grid.getX()) / grid.getWidth();
|
|
param.y = (param.event.zrenderY - grid.getY()) / grid.getHeight();
|
|
}
|
|
}
|
|
break;
|
|
/*
|
|
case ecConfig.EVENT.RESIZE :
|
|
case ecConfig.EVENT.DATA_CHANGED :
|
|
case ecConfig.EVENT.PIE_SELECTED :
|
|
case ecConfig.EVENT.MAP_SELECTED :
|
|
break;
|
|
*/
|
|
}
|
|
|
|
// 多图联动,只做自己的一级事件分发,避免级联事件循环
|
|
if (this._connected && fromMyself && this._curEventType === param.type) {
|
|
for (var c in this._connected) {
|
|
this._connected[c].connectedEventHandler(param);
|
|
}
|
|
// 分发完毕后复位
|
|
this._curEventType = null;
|
|
}
|
|
|
|
if (!fromMyself || (!this._connected && fromMyself)) { // 处理了完联动事件复位
|
|
this._curEventType = null;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 点击事件,响应zrender事件,包装后分发到Echarts层
|
|
*/
|
|
_onclick: function (param) {
|
|
callChartListMethodReverse(this, 'onclick', param);
|
|
|
|
if (param.target) {
|
|
var ecData = this._eventPackage(param.target);
|
|
if (ecData && ecData.seriesIndex != null) {
|
|
this._messageCenter.dispatch(
|
|
ecConfig.EVENT.CLICK,
|
|
param.event,
|
|
ecData,
|
|
this
|
|
);
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 双击事件,响应zrender事件,包装后分发到Echarts层
|
|
*/
|
|
_ondblclick: function (param) {
|
|
callChartListMethodReverse(this, 'ondblclick', param);
|
|
|
|
if (param.target) {
|
|
var ecData = this._eventPackage(param.target);
|
|
if (ecData && ecData.seriesIndex != null) {
|
|
this._messageCenter.dispatch(
|
|
ecConfig.EVENT.DBLCLICK,
|
|
param.event,
|
|
ecData,
|
|
this
|
|
);
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 鼠标移入事件,响应zrender事件,包装后分发到Echarts层
|
|
*/
|
|
_onmouseover: function (param) {
|
|
if (param.target) {
|
|
var ecData = this._eventPackage(param.target);
|
|
if (ecData && ecData.seriesIndex != null) {
|
|
this._messageCenter.dispatch(
|
|
ecConfig.EVENT.HOVER,
|
|
param.event,
|
|
ecData,
|
|
this
|
|
);
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 鼠标移出事件,响应zrender事件,包装后分发到Echarts层
|
|
*/
|
|
_onmouseout: function (param) {
|
|
if (param.target) {
|
|
var ecData = this._eventPackage(param.target);
|
|
if (ecData && ecData.seriesIndex != null) {
|
|
this._messageCenter.dispatch(
|
|
ecConfig.EVENT.MOUSEOUT,
|
|
param.event,
|
|
ecData,
|
|
this
|
|
);
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* dragstart回调,可计算特性实现
|
|
*/
|
|
_ondragstart: function (param) {
|
|
// 复位用于图表间通信拖拽标识
|
|
this._status = {
|
|
dragIn: false,
|
|
dragOut: false,
|
|
needRefresh: false
|
|
};
|
|
|
|
callChartListMethodReverse(this, 'ondragstart', param);
|
|
},
|
|
|
|
/**
|
|
* dragging回调,可计算特性实现
|
|
*/
|
|
_ondragenter: function (param) {
|
|
callChartListMethodReverse(this, 'ondragenter', param);
|
|
},
|
|
|
|
/**
|
|
* dragstart回调,可计算特性实现
|
|
*/
|
|
_ondragover: function (param) {
|
|
callChartListMethodReverse(this, 'ondragover', param);
|
|
},
|
|
|
|
/**
|
|
* dragstart回调,可计算特性实现
|
|
*/
|
|
_ondragleave: function (param) {
|
|
callChartListMethodReverse(this, 'ondragleave', param);
|
|
},
|
|
|
|
/**
|
|
* dragstart回调,可计算特性实现
|
|
*/
|
|
_ondrop: function (param) {
|
|
callChartListMethodReverse(this, 'ondrop', param, this._status);
|
|
this._island.ondrop(param, this._status);
|
|
},
|
|
|
|
/**
|
|
* dragdone回调 ,可计算特性实现
|
|
*/
|
|
_ondragend: function (param) {
|
|
callChartListMethodReverse(this, 'ondragend', param, this._status);
|
|
|
|
this._timeline && this._timeline.ondragend(param, this._status);
|
|
this._island.ondragend(param, this._status);
|
|
|
|
// 发生过重计算
|
|
if (this._status.needRefresh) {
|
|
this._syncBackupData(this._option);
|
|
|
|
var messageCenter = this._messageCenter;
|
|
messageCenter.dispatch(
|
|
ecConfig.EVENT.DATA_CHANGED,
|
|
param.event,
|
|
this._eventPackage(param.target),
|
|
this
|
|
);
|
|
messageCenter.dispatch(ecConfig.EVENT.REFRESH, null, null, this);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 图例选择响应
|
|
*/
|
|
_onlegendSelected: function (param) {
|
|
// 用于图表间通信
|
|
this._status.needRefresh = false;
|
|
callChartListMethodReverse(this, 'onlegendSelected', param, this._status);
|
|
|
|
if (this._status.needRefresh) {
|
|
this._messageCenter.dispatch(ecConfig.EVENT.REFRESH, null, null, this);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 数据区域缩放响应
|
|
*/
|
|
_ondataZoom: function (param) {
|
|
// 用于图表间通信
|
|
this._status.needRefresh = false;
|
|
callChartListMethodReverse(this, 'ondataZoom', param, this._status);
|
|
|
|
if (this._status.needRefresh) {
|
|
this._messageCenter.dispatch(ecConfig.EVENT.REFRESH, null, null, this);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 值域漫游响应
|
|
*/
|
|
_ondataRange: function (param) {
|
|
this._clearEffect();
|
|
// 用于图表间通信
|
|
this._status.needRefresh = false;
|
|
callChartListMethodReverse(this, 'ondataRange', param, this._status);
|
|
|
|
// 没有相互影响,直接刷新即可
|
|
if (this._status.needRefresh) {
|
|
this._zr.refreshNextFrame();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 动态类型切换响应
|
|
*/
|
|
_onmagicTypeChanged: function () {
|
|
this._clearEffect();
|
|
this._render(this._toolbox.getMagicOption());
|
|
},
|
|
|
|
/**
|
|
* 数据视图修改响应
|
|
*/
|
|
_ondataViewChanged: function (param) {
|
|
this._syncBackupData(param.option);
|
|
this._messageCenter.dispatch(
|
|
ecConfig.EVENT.DATA_CHANGED,
|
|
null,
|
|
param,
|
|
this
|
|
);
|
|
this._messageCenter.dispatch(ecConfig.EVENT.REFRESH, null, null, this);
|
|
},
|
|
|
|
/**
|
|
* tooltip与图表间通信
|
|
*/
|
|
_tooltipHover: function (param) {
|
|
var tipShape = [];
|
|
callChartListMethodReverse(this, 'ontooltipHover', param, tipShape);
|
|
},
|
|
|
|
/**
|
|
* 还原
|
|
*/
|
|
_onrestore: function () {
|
|
this.restore();
|
|
},
|
|
|
|
/**
|
|
* 刷新
|
|
*/
|
|
_onrefresh: function (param) {
|
|
this._refreshInside = true;
|
|
this.refresh(param);
|
|
this._refreshInside = false;
|
|
},
|
|
|
|
/**
|
|
* 数据修改后的反向同步dataZoom持有的备份数据
|
|
*/
|
|
_syncBackupData: function (curOption) {
|
|
this.component.dataZoom && this.component.dataZoom.syncBackupData(curOption);
|
|
},
|
|
|
|
/**
|
|
* 打包Echarts层的事件附件
|
|
*/
|
|
_eventPackage: function (target) {
|
|
if (target) {
|
|
var ecData = require('./util/ecData');
|
|
|
|
var seriesIndex = ecData.get(target, 'seriesIndex');
|
|
var dataIndex = ecData.get(target, 'dataIndex');
|
|
|
|
dataIndex = seriesIndex != -1 && this.component.dataZoom
|
|
? this.component.dataZoom.getRealDataIndex(
|
|
seriesIndex,
|
|
dataIndex
|
|
)
|
|
: dataIndex;
|
|
return {
|
|
seriesIndex: seriesIndex,
|
|
seriesName: (ecData.get(target, 'series') || {}).name,
|
|
dataIndex: dataIndex,
|
|
data: ecData.get(target, 'data'),
|
|
name: ecData.get(target, 'name'),
|
|
value: ecData.get(target, 'value'),
|
|
special: ecData.get(target, 'special')
|
|
};
|
|
}
|
|
return;
|
|
},
|
|
|
|
_noDataCheck: function(magicOption) {
|
|
var series = magicOption.series;
|
|
|
|
for (var i = 0, l = series.length; i < l; i++) {
|
|
if (series[i].type == ecConfig.CHART_TYPE_MAP
|
|
|| (series[i].data && series[i].data.length > 0)
|
|
|| (series[i].markPoint && series[i].markPoint.data && series[i].markPoint.data.length > 0)
|
|
|| (series[i].markLine && series[i].markLine.data && series[i].markLine.data.length > 0)
|
|
|| (series[i].nodes && series[i].nodes.length > 0)
|
|
|| (series[i].links && series[i].links.length > 0)
|
|
|| (series[i].matrix && series[i].matrix.length > 0)
|
|
|| (series[i].eventList && series[i].eventList.length > 0)
|
|
) {
|
|
return false; // 存在任意数据则为非空数据
|
|
}
|
|
}
|
|
var loadOption = (this._option && this._option.noDataLoadingOption)
|
|
|| this._themeConfig.noDataLoadingOption
|
|
|| ecConfig.noDataLoadingOption
|
|
|| {
|
|
text: (this._option && this._option.noDataText)
|
|
|| this._themeConfig.noDataText
|
|
|| ecConfig.noDataText,
|
|
effect: (this._option && this._option.noDataEffect)
|
|
|| this._themeConfig.noDataEffect
|
|
|| ecConfig.noDataEffect
|
|
};
|
|
// 空数据
|
|
this.clear();
|
|
this.showLoading(loadOption);
|
|
return true;
|
|
},
|
|
|
|
/**
|
|
* 图表渲染
|
|
*/
|
|
_render: function (magicOption) {
|
|
this._mergeGlobalConifg(magicOption);
|
|
|
|
if (this._noDataCheck(magicOption)) {
|
|
return;
|
|
}
|
|
|
|
var bgColor = magicOption.backgroundColor;
|
|
if (bgColor) {
|
|
if (!_canvasSupported
|
|
&& bgColor.indexOf('rgba') != -1
|
|
) {
|
|
// IE6~8对RGBA的处理,filter会带来其他颜色的影响
|
|
var cList = bgColor.split(',');
|
|
this.dom.style.filter = 'alpha(opacity=' +
|
|
cList[3].substring(0, cList[3].lastIndexOf(')')) * 100
|
|
+ ')';
|
|
cList.length = 3;
|
|
cList[0] = cList[0].replace('a', '');
|
|
this.dom.style.backgroundColor = cList.join(',') + ')';
|
|
}
|
|
else {
|
|
this.dom.style.backgroundColor = bgColor;
|
|
}
|
|
}
|
|
|
|
this._zr.clearAnimation();
|
|
this._chartList = [];
|
|
|
|
var chartLibrary = require('./chart');
|
|
var componentLibrary = require('./component');
|
|
|
|
if (magicOption.xAxis || magicOption.yAxis) {
|
|
magicOption.grid = magicOption.grid || {};
|
|
magicOption.dataZoom = magicOption.dataZoom || {};
|
|
}
|
|
|
|
var componentList = [
|
|
'title', 'legend', 'tooltip', 'dataRange', 'roamController',
|
|
'grid', 'dataZoom', 'xAxis', 'yAxis', 'polar'
|
|
];
|
|
|
|
var ComponentClass;
|
|
var componentType;
|
|
var component;
|
|
for (var i = 0, l = componentList.length; i < l; i++) {
|
|
componentType = componentList[i];
|
|
component = this.component[componentType];
|
|
|
|
if (magicOption[componentType]) {
|
|
if (component) {
|
|
component.refresh && component.refresh(magicOption);
|
|
}
|
|
else {
|
|
ComponentClass = componentLibrary.get(
|
|
/^[xy]Axis$/.test(componentType) ? 'axis' : componentType
|
|
);
|
|
component = new ComponentClass(
|
|
this._themeConfig, this._messageCenter, this._zr,
|
|
magicOption, this, componentType
|
|
);
|
|
this.component[componentType] = component;
|
|
}
|
|
this._chartList.push(component);
|
|
}
|
|
else if (component) {
|
|
component.dispose();
|
|
this.component[componentType] = null;
|
|
delete this.component[componentType];
|
|
}
|
|
}
|
|
|
|
var ChartClass;
|
|
var chartType;
|
|
var chart;
|
|
var chartMap = {}; // 记录已经初始化的图表
|
|
for (var i = 0, l = magicOption.series.length; i < l; i++) {
|
|
chartType = magicOption.series[i].type;
|
|
if (!chartType) {
|
|
console.error('series[' + i + '] chart type has not been defined.');
|
|
continue;
|
|
}
|
|
|
|
if (!chartMap[chartType]) {
|
|
chartMap[chartType] = true;
|
|
ChartClass = chartLibrary.get(chartType);
|
|
if (ChartClass) {
|
|
if (this.chart[chartType]) {
|
|
chart = this.chart[chartType];
|
|
chart.refresh(magicOption);
|
|
}
|
|
else {
|
|
chart = new ChartClass(
|
|
this._themeConfig, this._messageCenter, this._zr,
|
|
magicOption, this
|
|
);
|
|
}
|
|
this._chartList.push(chart);
|
|
this.chart[chartType] = chart;
|
|
}
|
|
else {
|
|
console.error(chartType + ' has not been required.');
|
|
}
|
|
}
|
|
}
|
|
|
|
// 已有实例但新option不带这类图表的实例释放
|
|
for (chartType in this.chart) {
|
|
if (chartType != ecConfig.CHART_TYPE_ISLAND && !chartMap[chartType]) {
|
|
this.chart[chartType].dispose();
|
|
this.chart[chartType] = null;
|
|
delete this.chart[chartType];
|
|
}
|
|
}
|
|
|
|
this.component.grid && this.component.grid.refixAxisShape(this.component);
|
|
|
|
this._island.refresh(magicOption);
|
|
this._toolbox.refresh(magicOption);
|
|
|
|
magicOption.animation && !magicOption.renderAsImage
|
|
? this._zr.refresh()
|
|
: this._zr.render();
|
|
|
|
var imgId = 'IMG' + this.id;
|
|
var img = document.getElementById(imgId);
|
|
if (magicOption.renderAsImage && _canvasSupported) {
|
|
// IE8- 不支持图片渲染形式
|
|
if (img) {
|
|
// 已经渲染过则更新显示
|
|
img.src = this.getDataURL(magicOption.renderAsImage);
|
|
}
|
|
else {
|
|
// 没有渲染过插入img dom
|
|
img = this.getImage(magicOption.renderAsImage);
|
|
img.id = imgId;
|
|
img.style.position = 'absolute';
|
|
img.style.left = 0;
|
|
img.style.top = 0;
|
|
this.dom.firstChild.appendChild(img);
|
|
}
|
|
this.un();
|
|
this._zr.un();
|
|
this._disposeChartList();
|
|
this._zr.clear();
|
|
}
|
|
else if (img) {
|
|
// 删除可能存在的img
|
|
img.parentNode.removeChild(img);
|
|
}
|
|
img = null;
|
|
|
|
this._option = magicOption;
|
|
},
|
|
|
|
/**
|
|
* 还原
|
|
*/
|
|
restore: function () {
|
|
this._clearEffect();
|
|
this._option = zrUtil.clone(this._optionRestore);
|
|
this._disposeChartList();
|
|
this._island.clear();
|
|
this._toolbox.reset(this._option, true);
|
|
this._render(this._option);
|
|
},
|
|
|
|
/**
|
|
* 刷新
|
|
* @param {Object=} param,可选参数,用于附带option,内部同步用,外部不建议带入数据修改,无法同步
|
|
*/
|
|
refresh: function (param) {
|
|
this._clearEffect();
|
|
param = param || {};
|
|
var magicOption = param.option;
|
|
|
|
// 外部调用的refresh且有option带入
|
|
if (!this._refreshInside && magicOption) {
|
|
// 做简单的差异合并去同步内部持有的数据克隆,不建议带入数据
|
|
// 开启数据区域缩放、拖拽重计算、数据视图可编辑模式情况下,当用户产生了数据变化后无法同步
|
|
// 如有带入option存在数据变化,请重新setOption
|
|
magicOption = this.getOption();
|
|
zrUtil.merge(magicOption, param.option, true);
|
|
zrUtil.merge(this._optionRestore, param.option, true);
|
|
this._toolbox.reset(magicOption);
|
|
}
|
|
|
|
this._island.refresh(magicOption);
|
|
this._toolbox.refresh(magicOption);
|
|
|
|
// 停止动画
|
|
this._zr.clearAnimation();
|
|
// 先来后到,安顺序刷新各种图表,图表内部refresh优化检查magicOption,无需更新则不更新~
|
|
for (var i = 0, l = this._chartList.length; i < l; i++) {
|
|
this._chartList[i].refresh && this._chartList[i].refresh(magicOption);
|
|
}
|
|
this.component.grid && this.component.grid.refixAxisShape(this.component);
|
|
this._zr.refresh();
|
|
},
|
|
|
|
/**
|
|
* 释放图表实例
|
|
*/
|
|
_disposeChartList: function () {
|
|
this._clearEffect();
|
|
|
|
// 停止动画
|
|
this._zr.clearAnimation();
|
|
|
|
var len = this._chartList.length;
|
|
while (len--) {
|
|
var chart = this._chartList[len];
|
|
|
|
if (chart) {
|
|
var chartType = chart.type;
|
|
this.chart[chartType] && delete this.chart[chartType];
|
|
this.component[chartType] && delete this.component[chartType];
|
|
chart.dispose && chart.dispose();
|
|
}
|
|
}
|
|
|
|
this._chartList = [];
|
|
},
|
|
|
|
/**
|
|
* 非图表全局属性merge~~
|
|
*/
|
|
_mergeGlobalConifg: function (magicOption) {
|
|
var mergeList = [
|
|
// 背景颜色
|
|
'backgroundColor',
|
|
|
|
// 拖拽重计算相关
|
|
'calculable', 'calculableColor', 'calculableHolderColor',
|
|
|
|
// 孤岛显示连接符
|
|
'nameConnector', 'valueConnector',
|
|
|
|
// 动画相关
|
|
'animation', 'animationThreshold',
|
|
'animationDuration', 'animationDurationUpdate',
|
|
'animationEasing', 'addDataAnimation',
|
|
|
|
// 默认标志图形类型列表
|
|
'symbolList',
|
|
|
|
// 降低图表内元素拖拽敏感度,单位ms,不建议外部干预
|
|
'DRAG_ENABLE_TIME'
|
|
];
|
|
|
|
var len = mergeList.length;
|
|
while (len--) {
|
|
var mergeItem = mergeList[len];
|
|
if (magicOption[mergeItem] == null) {
|
|
magicOption[mergeItem] = this._themeConfig[mergeItem] != null
|
|
? this._themeConfig[mergeItem]
|
|
: ecConfig[mergeItem];
|
|
}
|
|
}
|
|
|
|
// 数值系列的颜色列表,不传则采用内置颜色,可配数组,借用zrender实例注入,会有冲突风险,先这样
|
|
var themeColor = magicOption.color;
|
|
if (!(themeColor && themeColor.length)) {
|
|
themeColor = this._themeConfig.color || ecConfig.color;
|
|
}
|
|
|
|
this._zr.getColor = function (idx) {
|
|
var zrColor = require('zrender/tool/color');
|
|
return zrColor.getColor(idx, themeColor);
|
|
};
|
|
|
|
if (!_canvasSupported) {
|
|
// 不支持Canvas的强制关闭动画
|
|
magicOption.animation = false;
|
|
magicOption.addDataAnimation = false;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 万能接口,配置图表实例任何可配置选项,多次调用时option选项做merge处理
|
|
* @param {Object} option
|
|
* @param {boolean=} notMerge 多次调用时option选项是默认是合并(merge)的,
|
|
* 如果不需求,可以通过notMerger参数为true阻止与上次option的合并
|
|
*/
|
|
setOption: function (option, notMerge) {
|
|
if (!option.timeline) {
|
|
return this._setOption(option, notMerge);
|
|
}
|
|
else {
|
|
return this._setTimelineOption(option);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 万能接口,配置图表实例任何可配置选项,多次调用时option选项做merge处理
|
|
* @param {Object} option
|
|
* @param {boolean=} notMerge 多次调用时option选项是默认是合并(merge)的,
|
|
* 如果不需求,可以通过notMerger参数为true阻止与上次option的合并
|
|
* @param {boolean=} 默认false。keepTimeLine 表示从timeline组件调用而来,
|
|
* 表示当前行为是timeline的数据切换,保持timeline,
|
|
* 反之销毁timeline。 详见Issue #1601
|
|
*/
|
|
_setOption: function (option, notMerge, keepTimeLine) {
|
|
if (!notMerge && this._option) {
|
|
this._option = zrUtil.merge(
|
|
this.getOption(),
|
|
zrUtil.clone(option),
|
|
true
|
|
);
|
|
}
|
|
else {
|
|
this._option = zrUtil.clone(option);
|
|
!keepTimeLine && this._timeline && this._timeline.dispose();
|
|
}
|
|
|
|
this._optionRestore = zrUtil.clone(this._option);
|
|
|
|
if (!this._option.series || this._option.series.length === 0) {
|
|
this._zr.clear();
|
|
return;
|
|
}
|
|
|
|
if (this.component.dataZoom // 存在dataZoom控件
|
|
&& (this._option.dataZoom // 并且新option也存在
|
|
|| (this._option.toolbox
|
|
&& this._option.toolbox.feature
|
|
&& this._option.toolbox.feature.dataZoom
|
|
&& this._option.toolbox.feature.dataZoom.show
|
|
)
|
|
)
|
|
) {
|
|
// dataZoom同步数据
|
|
this.component.dataZoom.syncOption(this._option);
|
|
}
|
|
this._toolbox.reset(this._option);
|
|
this._render(this._option);
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* 返回内部持有的当前显示option克隆
|
|
*/
|
|
getOption: function () {
|
|
var magicOption = zrUtil.clone(this._option);
|
|
|
|
var self = this;
|
|
function restoreOption(prop) {
|
|
var restoreSource = self._optionRestore[prop];
|
|
|
|
if (restoreSource) {
|
|
if (restoreSource instanceof Array) {
|
|
var len = restoreSource.length;
|
|
while (len--) {
|
|
magicOption[prop][len].data = zrUtil.clone(
|
|
restoreSource[len].data
|
|
);
|
|
}
|
|
}
|
|
else {
|
|
magicOption[prop].data = zrUtil.clone(restoreSource.data);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 横轴数据还原
|
|
restoreOption('xAxis');
|
|
|
|
// 纵轴数据还原
|
|
restoreOption('yAxis');
|
|
|
|
// 系列数据还原
|
|
restoreOption('series');
|
|
|
|
return magicOption;
|
|
},
|
|
|
|
/**
|
|
* 数据设置快捷接口
|
|
* @param {Array} series
|
|
* @param {boolean=} notMerge 多次调用时option选项是默认是合并(merge)的,
|
|
* 如果不需求,可以通过notMerger参数为true阻止与上次option的合并。
|
|
*/
|
|
setSeries: function (series, notMerge) {
|
|
if (!notMerge) {
|
|
this.setOption({series: series});
|
|
}
|
|
else {
|
|
this._option.series = series;
|
|
this.setOption(this._option, notMerge);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* 返回内部持有的当前显示series克隆
|
|
*/
|
|
getSeries: function () {
|
|
return this.getOption().series;
|
|
},
|
|
|
|
/**
|
|
* timelineOption接口,配置图表实例任何可配置选项
|
|
* @param {Object} option
|
|
*/
|
|
_setTimelineOption: function(option) {
|
|
this._timeline && this._timeline.dispose();
|
|
var Timeline = require('./component/timeline');
|
|
var timeline = new Timeline(
|
|
this._themeConfig, this._messageCenter, this._zr, option, this
|
|
);
|
|
this._timeline = timeline;
|
|
this.component.timeline = this._timeline;
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* 动态数据添加
|
|
* 形参为单组数据参数,多组时为数据,内容同[seriesIdx, data, isShift, additionData]
|
|
* @param {number} seriesIdx 系列索引
|
|
* @param {number | Object} data 增加数据
|
|
* @param {boolean=} isHead 是否队头加入,默认,不指定或false时为队尾插入
|
|
* @param {boolean=} dataGrow 是否增长数据队列长度,默认,不指定或false时移出目标数组对位数据
|
|
* @param {string=} additionData 是否增加类目轴(饼图为图例)数据,附加操作同isHead和dataGrow
|
|
*/
|
|
addData: function (seriesIdx, data, isHead, dataGrow, additionData) {
|
|
var params = seriesIdx instanceof Array
|
|
? seriesIdx
|
|
: [[seriesIdx, data, isHead, dataGrow, additionData]];
|
|
|
|
//this._optionRestore 和 magicOption 都要同步
|
|
var magicOption = this.getOption();
|
|
var optionRestore = this._optionRestore;
|
|
var self = this;
|
|
for (var i = 0, l = params.length; i < l; i++) {
|
|
seriesIdx = params[i][0];
|
|
data = params[i][1];
|
|
isHead = params[i][2];
|
|
dataGrow = params[i][3];
|
|
additionData = params[i][4];
|
|
|
|
var seriesItem = optionRestore.series[seriesIdx];
|
|
var inMethod = isHead ? 'unshift' : 'push';
|
|
var outMethod = isHead ? 'pop' : 'shift';
|
|
if (seriesItem) {
|
|
var seriesItemData = seriesItem.data;
|
|
var mSeriesItemData = magicOption.series[seriesIdx].data;
|
|
|
|
seriesItemData[inMethod](data);
|
|
mSeriesItemData[inMethod](data);
|
|
if (!dataGrow) {
|
|
seriesItemData[outMethod]();
|
|
data = mSeriesItemData[outMethod]();
|
|
}
|
|
|
|
if (additionData != null) {
|
|
var legend;
|
|
var legendData;
|
|
|
|
if (seriesItem.type === ecConfig.CHART_TYPE_PIE
|
|
&& (legend = optionRestore.legend)
|
|
&& (legendData = legend.data)
|
|
) {
|
|
var mLegendData = magicOption.legend.data;
|
|
legendData[inMethod](additionData);
|
|
mLegendData[inMethod](additionData);
|
|
|
|
if (!dataGrow) {
|
|
var legendDataIdx = zrUtil.indexOf(legendData, data.name);
|
|
legendDataIdx != -1 && legendData.splice(legendDataIdx, 1);
|
|
|
|
legendDataIdx = zrUtil.indexOf(mLegendData, data.name);
|
|
legendDataIdx != -1 && mLegendData.splice(legendDataIdx, 1);
|
|
}
|
|
}
|
|
else if (optionRestore.xAxis != null && optionRestore.yAxis != null) {
|
|
// x轴类目
|
|
var axisData;
|
|
var mAxisData;
|
|
var axisIdx = seriesItem.xAxisIndex || 0;
|
|
|
|
if (optionRestore.xAxis[axisIdx].type == null
|
|
|| optionRestore.xAxis[axisIdx].type === 'category'
|
|
) {
|
|
axisData = optionRestore.xAxis[axisIdx].data;
|
|
mAxisData = magicOption.xAxis[axisIdx].data;
|
|
|
|
axisData[inMethod](additionData);
|
|
mAxisData[inMethod](additionData);
|
|
if (!dataGrow) {
|
|
axisData[outMethod]();
|
|
mAxisData[outMethod]();
|
|
}
|
|
}
|
|
|
|
// y轴类目
|
|
axisIdx = seriesItem.yAxisIndex || 0;
|
|
if (optionRestore.yAxis[axisIdx].type === 'category') {
|
|
axisData = optionRestore.yAxis[axisIdx].data;
|
|
mAxisData = magicOption.yAxis[axisIdx].data;
|
|
|
|
axisData[inMethod](additionData);
|
|
mAxisData[inMethod](additionData);
|
|
if (!dataGrow) {
|
|
axisData[outMethod]();
|
|
mAxisData[outMethod]();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 同步图表内状态,动画需要
|
|
this._option.series[seriesIdx].data = magicOption.series[seriesIdx].data;
|
|
}
|
|
}
|
|
|
|
this._zr.clearAnimation();
|
|
var chartList = this._chartList;
|
|
var chartAnimationCount = 0;
|
|
var chartAnimationDone = function () {
|
|
chartAnimationCount--;
|
|
if (chartAnimationCount === 0) {
|
|
animationDone();
|
|
}
|
|
};
|
|
for (var i = 0, l = chartList.length; i < l; i++) {
|
|
if (magicOption.addDataAnimation && chartList[i].addDataAnimation) {
|
|
chartAnimationCount++;
|
|
chartList[i].addDataAnimation(params, chartAnimationDone);
|
|
}
|
|
}
|
|
|
|
// dataZoom同步数据
|
|
this.component.dataZoom && this.component.dataZoom.syncOption(magicOption);
|
|
|
|
this._option = magicOption;
|
|
function animationDone() {
|
|
if (!self._zr) {
|
|
return; // 已经被释放
|
|
}
|
|
self._zr.clearAnimation();
|
|
for (var i = 0, l = chartList.length; i < l; i++) {
|
|
// 有addData动画就去掉过渡动画
|
|
chartList[i].motionlessOnce =
|
|
magicOption.addDataAnimation && chartList[i].addDataAnimation;
|
|
}
|
|
self._messageCenter.dispatch(
|
|
ecConfig.EVENT.REFRESH,
|
|
null,
|
|
{option: magicOption},
|
|
self
|
|
);
|
|
}
|
|
|
|
if (!magicOption.addDataAnimation) {
|
|
setTimeout(animationDone, 0);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* 动态[标注 | 标线]添加
|
|
* @param {number} seriesIdx 系列索引
|
|
* @param {Object} markData [标注 | 标线]对象,支持多个
|
|
*/
|
|
addMarkPoint: function (seriesIdx, markData) {
|
|
return this._addMark(seriesIdx, markData, 'markPoint');
|
|
},
|
|
|
|
addMarkLine: function (seriesIdx, markData) {
|
|
return this._addMark(seriesIdx, markData, 'markLine');
|
|
},
|
|
|
|
_addMark: function (seriesIdx, markData, markType) {
|
|
var series = this._option.series;
|
|
var seriesItem;
|
|
|
|
if (series && (seriesItem = series[seriesIdx])) {
|
|
var seriesR = this._optionRestore.series;
|
|
var seriesRItem = seriesR[seriesIdx];
|
|
var markOpt = seriesItem[markType];
|
|
var markOptR = seriesRItem[markType];
|
|
|
|
markOpt = seriesItem[markType] = markOpt || {data: []};
|
|
markOptR = seriesRItem[markType] = markOptR || {data: []};
|
|
|
|
for (var key in markData) {
|
|
if (key === 'data') {
|
|
// 数据concat
|
|
markOpt.data = markOpt.data.concat(markData.data);
|
|
markOptR.data = markOptR.data.concat(markData.data);
|
|
}
|
|
else if (typeof markData[key] != 'object' || markOpt[key] == null) {
|
|
// 简单类型或新值直接赋值
|
|
markOpt[key] = markOptR[key] = markData[key];
|
|
}
|
|
else {
|
|
// 非数据的复杂对象merge
|
|
zrUtil.merge(markOpt[key], markData[key], true);
|
|
zrUtil.merge(markOptR[key], markData[key], true);
|
|
}
|
|
}
|
|
|
|
var chart = this.chart[seriesItem.type];
|
|
chart && chart.addMark(seriesIdx, markData, markType);
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* 动态[标注 | 标线]删除
|
|
* @param {number} seriesIdx 系列索引
|
|
* @param {string} markName [标注 | 标线]名称
|
|
*/
|
|
delMarkPoint: function (seriesIdx, markName) {
|
|
return this._delMark(seriesIdx, markName, 'markPoint');
|
|
},
|
|
|
|
delMarkLine: function (seriesIdx, markName) {
|
|
return this._delMark(seriesIdx, markName, 'markLine');
|
|
},
|
|
|
|
_delMark: function (seriesIdx, markName, markType) {
|
|
var series = this._option.series;
|
|
var seriesItem;
|
|
var mark;
|
|
var dataArray;
|
|
|
|
if (!(
|
|
series
|
|
&& (seriesItem = series[seriesIdx])
|
|
&& (mark = seriesItem[markType])
|
|
&& (dataArray = mark.data)
|
|
)
|
|
) {
|
|
return this;
|
|
}
|
|
|
|
markName = markName.split(' > ');
|
|
var targetIndex = -1;
|
|
|
|
for (var i = 0, l = dataArray.length; i < l; i++) {
|
|
var dataItem = dataArray[i];
|
|
if (dataItem instanceof Array) {
|
|
if (dataItem[0].name === markName[0]
|
|
&& dataItem[1].name === markName[1]
|
|
) {
|
|
targetIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
else if (dataItem.name === markName[0]) {
|
|
targetIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (targetIndex > -1) {
|
|
dataArray.splice(targetIndex, 1);
|
|
this._optionRestore.series[seriesIdx][markType].data.splice(targetIndex, 1);
|
|
|
|
var chart = this.chart[seriesItem.type];
|
|
chart && chart.delMark(seriesIdx, markName.join(' > '), markType);
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* 获取当前dom
|
|
*/
|
|
getDom: function () {
|
|
return this.dom;
|
|
},
|
|
|
|
/**
|
|
* 获取当前zrender实例,可用于添加额为的shape和深度控制
|
|
*/
|
|
getZrender: function () {
|
|
return this._zr;
|
|
},
|
|
|
|
/**
|
|
* 获取Base64图片dataURL
|
|
* @param {string} imgType 图片类型,支持png|jpeg,默认为png
|
|
* @return imgDataURL
|
|
*/
|
|
getDataURL: function (imgType) {
|
|
if (!_canvasSupported) {
|
|
return '';
|
|
}
|
|
|
|
if (this._chartList.length === 0) {
|
|
// 渲染为图片
|
|
var imgId = 'IMG' + this.id;
|
|
var img = document.getElementById(imgId);
|
|
if (img) {
|
|
return img.src;
|
|
}
|
|
}
|
|
|
|
// 清除可能存在的tooltip元素
|
|
var tooltip = this.component.tooltip;
|
|
tooltip && tooltip.hideTip();
|
|
|
|
switch (imgType) {
|
|
case 'jpeg':
|
|
break;
|
|
default:
|
|
imgType = 'png';
|
|
}
|
|
|
|
var bgColor = this._option.backgroundColor;
|
|
if (bgColor && bgColor.replace(' ','') === 'rgba(0,0,0,0)') {
|
|
bgColor = '#fff';
|
|
}
|
|
|
|
return this._zr.toDataURL('image/' + imgType, bgColor);
|
|
},
|
|
|
|
/**
|
|
* 获取img
|
|
* @param {string} imgType 图片类型,支持png|jpeg,默认为png
|
|
* @return img dom
|
|
*/
|
|
getImage: function (imgType) {
|
|
var title = this._optionRestore.title;
|
|
var imgDom = document.createElement('img');
|
|
imgDom.src = this.getDataURL(imgType);
|
|
imgDom.title = (title && title.text) || 'ECharts';
|
|
return imgDom;
|
|
},
|
|
|
|
/**
|
|
* 获取多图联动的Base64图片dataURL
|
|
* @param {string} imgType 图片类型,支持png|jpeg,默认为png
|
|
* @return imgDataURL
|
|
*/
|
|
getConnectedDataURL: function (imgType) {
|
|
if (!this.isConnected()) {
|
|
return this.getDataURL(imgType);
|
|
}
|
|
|
|
var tempDom = this.dom;
|
|
var imgList = {
|
|
'self': {
|
|
img: this.getDataURL(imgType),
|
|
left: tempDom.offsetLeft,
|
|
top: tempDom.offsetTop,
|
|
right: tempDom.offsetLeft + tempDom.offsetWidth,
|
|
bottom: tempDom.offsetTop + tempDom.offsetHeight
|
|
}
|
|
};
|
|
|
|
var minLeft = imgList.self.left;
|
|
var minTop = imgList.self.top;
|
|
var maxRight = imgList.self.right;
|
|
var maxBottom = imgList.self.bottom;
|
|
|
|
for (var c in this._connected) {
|
|
tempDom = this._connected[c].getDom();
|
|
imgList[c] = {
|
|
img: this._connected[c].getDataURL(imgType),
|
|
left: tempDom.offsetLeft,
|
|
top: tempDom.offsetTop,
|
|
right: tempDom.offsetLeft + tempDom.offsetWidth,
|
|
bottom: tempDom.offsetTop + tempDom.offsetHeight
|
|
};
|
|
|
|
minLeft = Math.min(minLeft, imgList[c].left);
|
|
minTop = Math.min(minTop, imgList[c].top);
|
|
maxRight = Math.max(maxRight, imgList[c].right);
|
|
maxBottom = Math.max(maxBottom, imgList[c].bottom);
|
|
}
|
|
|
|
var zrDom = document.createElement('div');
|
|
zrDom.style.position = 'absolute';
|
|
zrDom.style.left = '-4000px';
|
|
zrDom.style.width = (maxRight - minLeft) + 'px';
|
|
zrDom.style.height = (maxBottom - minTop) + 'px';
|
|
document.body.appendChild(zrDom);
|
|
|
|
var zrImg = require('zrender').init(zrDom);
|
|
|
|
var ImageShape = require('zrender/shape/Image');
|
|
for (var c in imgList) {
|
|
zrImg.addShape(new ImageShape({
|
|
style: {
|
|
x: imgList[c].left - minLeft,
|
|
y: imgList[c].top - minTop,
|
|
image: imgList[c].img
|
|
}
|
|
}));
|
|
}
|
|
|
|
zrImg.render();
|
|
var bgColor = this._option.backgroundColor;
|
|
if (bgColor && bgColor.replace(/ /g, '') === 'rgba(0,0,0,0)') {
|
|
bgColor = '#fff';
|
|
}
|
|
|
|
var image = zrImg.toDataURL('image/png', bgColor);
|
|
|
|
setTimeout(function () {
|
|
zrImg.dispose();
|
|
zrDom.parentNode.removeChild(zrDom);
|
|
zrDom = null;
|
|
}, 100);
|
|
|
|
return image;
|
|
},
|
|
|
|
/**
|
|
* 获取多图联动的img
|
|
* @param {string} imgType 图片类型,支持png|jpeg,默认为png
|
|
* @return img dom
|
|
*/
|
|
getConnectedImage: function (imgType) {
|
|
var title = this._optionRestore.title;
|
|
var imgDom = document.createElement('img');
|
|
imgDom.src = this.getConnectedDataURL(imgType);
|
|
imgDom.title = (title && title.text) || 'ECharts';
|
|
return imgDom;
|
|
},
|
|
|
|
/**
|
|
* 外部接口绑定事件
|
|
* @param {Object} eventName 事件名称
|
|
* @param {Object} eventListener 事件响应函数
|
|
*/
|
|
on: function (eventName, eventListener) {
|
|
this._messageCenterOutSide.bind(eventName, eventListener, this);
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* 外部接口解除事件绑定
|
|
* @param {Object} eventName 事件名称
|
|
* @param {Object} eventListener 事件响应函数
|
|
*/
|
|
un: function (eventName, eventListener) {
|
|
this._messageCenterOutSide.unbind(eventName, eventListener);
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* 多图联动
|
|
* @param connectTarget{ECharts | Array <ECharts>} connectTarget 联动目标
|
|
*/
|
|
connect: function (connectTarget) {
|
|
if (!connectTarget) {
|
|
return this;
|
|
}
|
|
|
|
if (!this._connected) {
|
|
this._connected = {};
|
|
}
|
|
|
|
if (connectTarget instanceof Array) {
|
|
for (var i = 0, l = connectTarget.length; i < l; i++) {
|
|
this._connected[connectTarget[i].id] = connectTarget[i];
|
|
}
|
|
}
|
|
else {
|
|
this._connected[connectTarget.id] = connectTarget;
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* 解除多图联动
|
|
* @param connectTarget{ECharts | Array <ECharts>} connectTarget 解除联动目标
|
|
*/
|
|
disConnect: function (connectTarget) {
|
|
if (!connectTarget || !this._connected) {
|
|
return this;
|
|
}
|
|
|
|
if (connectTarget instanceof Array) {
|
|
for (var i = 0, l = connectTarget.length; i < l; i++) {
|
|
delete this._connected[connectTarget[i].id];
|
|
}
|
|
}
|
|
else {
|
|
delete this._connected[connectTarget.id];
|
|
}
|
|
|
|
for (var k in this._connected) {
|
|
return k, this; // 非空
|
|
}
|
|
|
|
// 空,转为标志位
|
|
this._connected = false;
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* 联动事件响应
|
|
*/
|
|
connectedEventHandler: function (param) {
|
|
if (param.__echartsId != this.id) {
|
|
// 来自其他联动图表的事件
|
|
this._onevent(param);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 是否存在多图联动
|
|
*/
|
|
isConnected: function () {
|
|
return !!this._connected;
|
|
},
|
|
|
|
/**
|
|
* 显示loading过渡
|
|
* @param {Object} loadingOption
|
|
*/
|
|
showLoading: function (loadingOption) {
|
|
var effectList = {
|
|
bar: require('zrender/loadingEffect/Bar'),
|
|
bubble: require('zrender/loadingEffect/Bubble'),
|
|
dynamicLine: require('zrender/loadingEffect/DynamicLine'),
|
|
ring: require('zrender/loadingEffect/Ring'),
|
|
spin: require('zrender/loadingEffect/Spin'),
|
|
whirling: require('zrender/loadingEffect/Whirling')
|
|
};
|
|
this._toolbox.hideDataView();
|
|
|
|
loadingOption = loadingOption || {};
|
|
|
|
var textStyle = loadingOption.textStyle || {};
|
|
loadingOption.textStyle = textStyle;
|
|
|
|
var finalTextStyle = zrUtil.merge(
|
|
zrUtil.merge(
|
|
zrUtil.clone(textStyle),
|
|
this._themeConfig.textStyle
|
|
),
|
|
ecConfig.textStyle
|
|
);
|
|
|
|
textStyle.textFont = finalTextStyle.fontStyle + ' '
|
|
+ finalTextStyle.fontWeight + ' '
|
|
+ finalTextStyle.fontSize + 'px '
|
|
+ finalTextStyle.fontFamily;
|
|
|
|
textStyle.text = loadingOption.text
|
|
|| (this._option && this._option.loadingText)
|
|
|| this._themeConfig.loadingText
|
|
|| ecConfig.loadingText;
|
|
|
|
if (loadingOption.x != null) {
|
|
textStyle.x = loadingOption.x;
|
|
}
|
|
if (loadingOption.y != null) {
|
|
textStyle.y = loadingOption.y;
|
|
}
|
|
|
|
loadingOption.effectOption = loadingOption.effectOption || {};
|
|
loadingOption.effectOption.textStyle = textStyle;
|
|
|
|
var Effect = loadingOption.effect;
|
|
if (typeof Effect === 'string' || Effect == null) {
|
|
Effect = effectList[
|
|
loadingOption.effect
|
|
|| (this._option && this._option.loadingEffect)
|
|
|| this._themeConfig.loadingEffect
|
|
|| ecConfig.loadingEffect
|
|
]
|
|
|| effectList.spin;
|
|
}
|
|
this._zr.showLoading(new Effect(loadingOption.effectOption));
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* 隐藏loading过渡
|
|
*/
|
|
hideLoading: function () {
|
|
this._zr.hideLoading();
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* 主题设置
|
|
*/
|
|
setTheme: function (theme) {
|
|
if (theme) {
|
|
if (typeof theme === 'string') {
|
|
// 默认主题
|
|
switch (theme) {
|
|
case 'macarons':
|
|
theme = require('./theme/macarons');
|
|
break;
|
|
case 'infographic':
|
|
theme = require('./theme/infographic');
|
|
break;
|
|
default:
|
|
theme = {}; // require('./theme/default');
|
|
}
|
|
}
|
|
else {
|
|
theme = theme || {};
|
|
}
|
|
|
|
// // 复位默认配置
|
|
// // this._themeConfig会被别的对象引用持有
|
|
// // 所以不能改成this._themeConfig = {};
|
|
// for (var key in this._themeConfig) {
|
|
// delete this._themeConfig[key];
|
|
// }
|
|
// for (var key in ecConfig) {
|
|
// this._themeConfig[key] = zrUtil.clone(ecConfig[key]);
|
|
// }
|
|
|
|
// // 颜色数组随theme,不merge
|
|
// theme.color && (this._themeConfig.color = []);
|
|
|
|
// // 默认标志图形类型列表,不merge
|
|
// theme.symbolList && (this._themeConfig.symbolList = []);
|
|
|
|
// // 应用新主题
|
|
// zrUtil.merge(this._themeConfig, zrUtil.clone(theme), true);
|
|
this._themeConfig = theme;
|
|
}
|
|
|
|
if (!_canvasSupported) { // IE8-
|
|
var textStyle = this._themeConfig.textStyle;
|
|
textStyle && textStyle.fontFamily && textStyle.fontFamily2
|
|
&& (textStyle.fontFamily = textStyle.fontFamily2);
|
|
|
|
textStyle = ecConfig.textStyle;
|
|
textStyle.fontFamily = textStyle.fontFamily2;
|
|
}
|
|
|
|
this._timeline && this._timeline.setTheme(true);
|
|
this._optionRestore && this.restore();
|
|
},
|
|
|
|
/**
|
|
* 视图区域大小变化更新,不默认绑定,供使用方按需调用
|
|
*/
|
|
resize: function () {
|
|
var self = this;
|
|
return function(){
|
|
self._clearEffect();
|
|
self._zr.resize();
|
|
if (self._option && self._option.renderAsImage && _canvasSupported) {
|
|
// 渲染为图片重走render模式
|
|
self._render(self._option);
|
|
return self;
|
|
}
|
|
// 停止动画
|
|
self._zr.clearAnimation();
|
|
self._island.resize();
|
|
self._toolbox.resize();
|
|
self._timeline && self._timeline.resize();
|
|
// 先来后到,不能仅刷新自己,也不能在上一个循环中刷新,如坐标系数据改变会影响其他图表的大小
|
|
// 所以安顺序刷新各种图表,图表内部refresh优化无需更新则不更新~
|
|
for (var i = 0, l = self._chartList.length; i < l; i++) {
|
|
self._chartList[i].resize && self._chartList[i].resize();
|
|
}
|
|
self.component.grid && self.component.grid.refixAxisShape(self.component);
|
|
self._zr.refresh();
|
|
self._messageCenter.dispatch(ecConfig.EVENT.RESIZE, null, null, self);
|
|
return self;
|
|
};
|
|
},
|
|
|
|
_clearEffect: function() {
|
|
this._zr.modLayer(ecConfig.EFFECT_ZLEVEL, { motionBlur: false });
|
|
this._zr.painter.clearLayer(ecConfig.EFFECT_ZLEVEL);
|
|
},
|
|
|
|
/**
|
|
* 清除已渲染内容 ,clear后echarts实例可用
|
|
*/
|
|
clear: function () {
|
|
this._disposeChartList();
|
|
this._zr.clear();
|
|
this._option = {};
|
|
this._optionRestore = {};
|
|
this.dom.style.backgroundColor = null;
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* 释放,dispose后echarts实例不可用
|
|
*/
|
|
dispose: function () {
|
|
var key = this.dom.getAttribute(DOM_ATTRIBUTE_KEY);
|
|
key && delete _instances[key];
|
|
|
|
this._island.dispose();
|
|
this._toolbox.dispose();
|
|
this._timeline && this._timeline.dispose();
|
|
this._messageCenter.unbind();
|
|
this.clear();
|
|
this._zr.dispose();
|
|
this._zr = null;
|
|
}
|
|
};
|
|
|
|
return self;
|
|
});
|