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.

1758 lines
63 KiB

  1. /*!
  2. * ECharts, a javascript interactive chart library.
  3. *
  4. * Copyright (c) 2015, Baidu Inc.
  5. * All rights reserved.
  6. *
  7. * LICENSE
  8. * https://github.com/ecomfe/echarts/blob/master/LICENSE.txt
  9. */
  10. /**
  11. * echarts
  12. *
  13. * @desc echarts基于Canvas纯Javascript图表库提供直观生动可交互可个性化定制的数据统计图表
  14. * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
  15. *
  16. */
  17. define(function (require) {
  18. var ecConfig = require('./config');
  19. var zrUtil = require('zrender/tool/util');
  20. var zrEvent = require('zrender/tool/event');
  21. var self = {};
  22. var _canvasSupported = require('zrender/tool/env').canvasSupported;
  23. var _idBase = new Date() - 0;
  24. var _instances = {}; // ECharts实例map索引
  25. var DOM_ATTRIBUTE_KEY = '_echarts_instance_';
  26. self.version = '2.2.7';
  27. self.dependencies = {
  28. zrender: '2.1.1'
  29. };
  30. /**
  31. * 入口方法
  32. */
  33. self.init = function (dom, theme) {
  34. var zrender = require('zrender');
  35. if ((zrender.version.replace('.', '') - 0) < (self.dependencies.zrender.replace('.', '') - 0)) {
  36. console.error(
  37. 'ZRender ' + zrender.version
  38. + ' is too old for ECharts ' + self.version
  39. + '. Current version need ZRender '
  40. + self.dependencies.zrender + '+'
  41. );
  42. }
  43. dom = dom instanceof Array ? dom[0] : dom;
  44. // dom与echarts实例映射索引
  45. var key = dom.getAttribute(DOM_ATTRIBUTE_KEY);
  46. if (!key) {
  47. key = _idBase++;
  48. dom.setAttribute(DOM_ATTRIBUTE_KEY, key);
  49. }
  50. if (_instances[key]) {
  51. // 同一个dom上多次init,自动释放已有实例
  52. _instances[key].dispose();
  53. }
  54. _instances[key] = new Echarts(dom);
  55. _instances[key].id = key;
  56. _instances[key].canvasSupported = _canvasSupported;
  57. _instances[key].setTheme(theme);
  58. return _instances[key];
  59. };
  60. /**
  61. * 通过id获得ECharts实例id可在实例化后读取
  62. */
  63. self.getInstanceById = function (key) {
  64. return _instances[key];
  65. };
  66. /**
  67. * 消息中心
  68. */
  69. function MessageCenter() {
  70. zrEvent.Dispatcher.call(this);
  71. }
  72. zrUtil.merge(MessageCenter.prototype, zrEvent.Dispatcher.prototype, true);
  73. /**
  74. * 基于zrender实现Echarts接口层
  75. * @param {HtmlElement} dom 必要
  76. */
  77. function Echarts(dom) {
  78. // Fxxk IE11 for breaking initialization without a warrant;
  79. // Just set something to let it be!
  80. // by kener 2015-01-09
  81. dom.innerHTML = '';
  82. this._themeConfig = {}; // zrUtil.clone(ecConfig);
  83. this.dom = dom;
  84. // this._zr;
  85. // this._option; // curOption clone
  86. // this._optionRestore; // for restore;
  87. // this._island;
  88. // this._toolbox;
  89. // this._timeline;
  90. // this._refreshInside; // 内部刷新标志位
  91. this._connected = false;
  92. this._status = { // 用于图表间通信
  93. dragIn: false,
  94. dragOut: false,
  95. needRefresh: false
  96. };
  97. this._curEventType = false; // 破循环信号灯
  98. this._chartList = []; // 图表实例
  99. this._messageCenter = new MessageCenter();
  100. this._messageCenterOutSide = new MessageCenter(); // Echarts层的外部消息中心,做Echarts层的消息转发
  101. // resize方法经常被绑定到window.resize上,闭包一个this
  102. this.resize = this.resize();
  103. // 初始化::构造函数
  104. this._init();
  105. }
  106. /**
  107. * ZRender EVENT
  108. *
  109. * @inner
  110. * @const
  111. * @type {Object}
  112. */
  113. var ZR_EVENT = require('zrender/config').EVENT;
  114. /**
  115. * 要绑定监听的zrender事件列表
  116. *
  117. * @const
  118. * @inner
  119. * @type {Array}
  120. */
  121. var ZR_EVENT_LISTENS = [
  122. 'CLICK', 'DBLCLICK', 'MOUSEOVER', 'MOUSEOUT',
  123. 'DRAGSTART', 'DRAGEND', 'DRAGENTER', 'DRAGOVER', 'DRAGLEAVE', 'DROP'
  124. ];
  125. /**
  126. * 对echarts的实例中的chartList属性成员逐个进行方法调用遍历顺序为逆序
  127. * 由于在事件触发的默认行为处理中多次用到相同逻辑所以抽象了该方法
  128. * 由于所有的调用场景里最多只有两个参数基于性能和体积考虑这里就不使用call或者apply了
  129. *
  130. * @inner
  131. * @param {ECharts} ecInstance ECharts实例
  132. * @param {string} methodName 要调用的方法名
  133. * @param {*} arg0 调用参数1
  134. * @param {*} arg1 调用参数2
  135. * @param {*} arg2 调用参数3
  136. */
  137. function callChartListMethodReverse(ecInstance, methodName, arg0, arg1, arg2) {
  138. var chartList = ecInstance._chartList;
  139. var len = chartList.length;
  140. while (len--) {
  141. var chart = chartList[len];
  142. if (typeof chart[methodName] === 'function') {
  143. chart[methodName](arg0, arg1, arg2);
  144. }
  145. }
  146. }
  147. Echarts.prototype = {
  148. /**
  149. * 初始化::构造函数
  150. */
  151. _init: function () {
  152. var self = this;
  153. var _zr = require('zrender').init(this.dom);
  154. this._zr = _zr;
  155. // wrap: n,e,d,t for name event data this
  156. this._messageCenter.dispatch = function(type, event, eventPackage, that) {
  157. eventPackage = eventPackage || {};
  158. eventPackage.type = type;
  159. eventPackage.event = event;
  160. self._messageCenter.dispatchWithContext(type, eventPackage, that);
  161. self._messageCenterOutSide.dispatchWithContext(type, eventPackage, that);
  162. // 如下注掉的代码,@see: https://github.com/ecomfe/echarts-discuss/issues/3
  163. // if (type != 'HOVER' && type != 'MOUSEOUT') { // 频繁事件直接抛出
  164. // setTimeout(function(){
  165. // self._messageCenterOutSide.dispatchWithContext(
  166. // type, eventPackage, that
  167. // );
  168. // },50);
  169. // }
  170. // else {
  171. // self._messageCenterOutSide.dispatchWithContext(
  172. // type, eventPackage, that
  173. // );
  174. // }
  175. };
  176. this._onevent = function(param){
  177. return self.__onevent(param);
  178. };
  179. for (var e in ecConfig.EVENT) {
  180. if (e != 'CLICK' && e != 'DBLCLICK'
  181. && e != 'HOVER' && e != 'MOUSEOUT' && e != 'MAP_ROAM'
  182. ) {
  183. this._messageCenter.bind(ecConfig.EVENT[e], this._onevent, this);
  184. }
  185. }
  186. var eventBehaviors = {};
  187. this._onzrevent = function (param) {
  188. return self[eventBehaviors[ param.type ]](param);
  189. };
  190. // 挂载关心的事件
  191. for (var i = 0, len = ZR_EVENT_LISTENS.length; i < len; i++) {
  192. var eventName = ZR_EVENT_LISTENS[i];
  193. var eventValue = ZR_EVENT[eventName];
  194. eventBehaviors[eventValue] = '_on' + eventName.toLowerCase();
  195. _zr.on(eventValue, this._onzrevent);
  196. }
  197. this.chart = {}; // 图表索引
  198. this.component = {}; // 组件索引
  199. // 内置图表
  200. // 孤岛
  201. var Island = require('./chart/island');
  202. this._island = new Island(this._themeConfig, this._messageCenter, _zr, {}, this);
  203. this.chart.island = this._island;
  204. // 内置通用组件
  205. // 工具箱
  206. var Toolbox = require('./component/toolbox');
  207. this._toolbox = new Toolbox(this._themeConfig, this._messageCenter, _zr, {}, this);
  208. this.component.toolbox = this._toolbox;
  209. var componentLibrary = require('./component');
  210. componentLibrary.define('title', require('./component/title'));
  211. componentLibrary.define('tooltip', require('./component/tooltip'));
  212. componentLibrary.define('legend', require('./component/legend'));
  213. if (_zr.getWidth() === 0 || _zr.getHeight() === 0) {
  214. console.error('Dom’s width & height should be ready before init.');
  215. }
  216. },
  217. /**
  218. * ECharts事件处理中心
  219. */
  220. __onevent: function (param){
  221. param.__echartsId = param.__echartsId || this.id;
  222. // 来自其他联动图表的事件
  223. var fromMyself = (param.__echartsId === this.id);
  224. if (!this._curEventType) {
  225. this._curEventType = param.type;
  226. }
  227. switch (param.type) {
  228. case ecConfig.EVENT.LEGEND_SELECTED :
  229. this._onlegendSelected(param);
  230. break;
  231. case ecConfig.EVENT.DATA_ZOOM :
  232. if (!fromMyself) {
  233. var dz = this.component.dataZoom;
  234. if (dz) {
  235. dz.silence(true);
  236. dz.absoluteZoom(param.zoom);
  237. dz.silence(false);
  238. }
  239. }
  240. this._ondataZoom(param);
  241. break;
  242. case ecConfig.EVENT.DATA_RANGE :
  243. fromMyself && this._ondataRange(param);
  244. break;
  245. case ecConfig.EVENT.MAGIC_TYPE_CHANGED :
  246. if (!fromMyself) {
  247. var tb = this.component.toolbox;
  248. if (tb) {
  249. tb.silence(true);
  250. tb.setMagicType(param.magicType);
  251. tb.silence(false);
  252. }
  253. }
  254. this._onmagicTypeChanged(param);
  255. break;
  256. case ecConfig.EVENT.DATA_VIEW_CHANGED :
  257. fromMyself && this._ondataViewChanged(param);
  258. break;
  259. case ecConfig.EVENT.TOOLTIP_HOVER :
  260. fromMyself && this._tooltipHover(param);
  261. break;
  262. case ecConfig.EVENT.RESTORE :
  263. this._onrestore();
  264. break;
  265. case ecConfig.EVENT.REFRESH :
  266. fromMyself && this._onrefresh(param);
  267. break;
  268. // 鼠标同步
  269. case ecConfig.EVENT.TOOLTIP_IN_GRID :
  270. case ecConfig.EVENT.TOOLTIP_OUT_GRID :
  271. if (!fromMyself) {
  272. // 只处理来自外部的鼠标同步
  273. var grid = this.component.grid;
  274. if (grid) {
  275. this._zr.trigger(
  276. 'mousemove',
  277. {
  278. connectTrigger: true,
  279. zrenderX: grid.getX() + param.x * grid.getWidth(),
  280. zrenderY: grid.getY() + param.y * grid.getHeight()
  281. }
  282. );
  283. }
  284. }
  285. else if (this._connected) {
  286. // 来自自己,并且存在多图联动,空间坐标映射修改参数分发
  287. var grid = this.component.grid;
  288. if (grid) {
  289. param.x = (param.event.zrenderX - grid.getX()) / grid.getWidth();
  290. param.y = (param.event.zrenderY - grid.getY()) / grid.getHeight();
  291. }
  292. }
  293. break;
  294. /*
  295. case ecConfig.EVENT.RESIZE :
  296. case ecConfig.EVENT.DATA_CHANGED :
  297. case ecConfig.EVENT.PIE_SELECTED :
  298. case ecConfig.EVENT.MAP_SELECTED :
  299. break;
  300. */
  301. }
  302. // 多图联动,只做自己的一级事件分发,避免级联事件循环
  303. if (this._connected && fromMyself && this._curEventType === param.type) {
  304. for (var c in this._connected) {
  305. this._connected[c].connectedEventHandler(param);
  306. }
  307. // 分发完毕后复位
  308. this._curEventType = null;
  309. }
  310. if (!fromMyself || (!this._connected && fromMyself)) { // 处理了完联动事件复位
  311. this._curEventType = null;
  312. }
  313. },
  314. /**
  315. * 点击事件响应zrender事件包装后分发到Echarts层
  316. */
  317. _onclick: function (param) {
  318. callChartListMethodReverse(this, 'onclick', param);
  319. if (param.target) {
  320. var ecData = this._eventPackage(param.target);
  321. if (ecData && ecData.seriesIndex != null) {
  322. this._messageCenter.dispatch(
  323. ecConfig.EVENT.CLICK,
  324. param.event,
  325. ecData,
  326. this
  327. );
  328. }
  329. }
  330. },
  331. /**
  332. * 双击事件响应zrender事件包装后分发到Echarts层
  333. */
  334. _ondblclick: function (param) {
  335. callChartListMethodReverse(this, 'ondblclick', param);
  336. if (param.target) {
  337. var ecData = this._eventPackage(param.target);
  338. if (ecData && ecData.seriesIndex != null) {
  339. this._messageCenter.dispatch(
  340. ecConfig.EVENT.DBLCLICK,
  341. param.event,
  342. ecData,
  343. this
  344. );
  345. }
  346. }
  347. },
  348. /**
  349. * 鼠标移入事件响应zrender事件包装后分发到Echarts层
  350. */
  351. _onmouseover: function (param) {
  352. if (param.target) {
  353. var ecData = this._eventPackage(param.target);
  354. if (ecData && ecData.seriesIndex != null) {
  355. this._messageCenter.dispatch(
  356. ecConfig.EVENT.HOVER,
  357. param.event,
  358. ecData,
  359. this
  360. );
  361. }
  362. }
  363. },
  364. /**
  365. * 鼠标移出事件响应zrender事件包装后分发到Echarts层
  366. */
  367. _onmouseout: function (param) {
  368. if (param.target) {
  369. var ecData = this._eventPackage(param.target);
  370. if (ecData && ecData.seriesIndex != null) {
  371. this._messageCenter.dispatch(
  372. ecConfig.EVENT.MOUSEOUT,
  373. param.event,
  374. ecData,
  375. this
  376. );
  377. }
  378. }
  379. },
  380. /**
  381. * dragstart回调可计算特性实现
  382. */
  383. _ondragstart: function (param) {
  384. // 复位用于图表间通信拖拽标识
  385. this._status = {
  386. dragIn: false,
  387. dragOut: false,
  388. needRefresh: false
  389. };
  390. callChartListMethodReverse(this, 'ondragstart', param);
  391. },
  392. /**
  393. * dragging回调可计算特性实现
  394. */
  395. _ondragenter: function (param) {
  396. callChartListMethodReverse(this, 'ondragenter', param);
  397. },
  398. /**
  399. * dragstart回调可计算特性实现
  400. */
  401. _ondragover: function (param) {
  402. callChartListMethodReverse(this, 'ondragover', param);
  403. },
  404. /**
  405. * dragstart回调可计算特性实现
  406. */
  407. _ondragleave: function (param) {
  408. callChartListMethodReverse(this, 'ondragleave', param);
  409. },
  410. /**
  411. * dragstart回调可计算特性实现
  412. */
  413. _ondrop: function (param) {
  414. callChartListMethodReverse(this, 'ondrop', param, this._status);
  415. this._island.ondrop(param, this._status);
  416. },
  417. /**
  418. * dragdone回调 可计算特性实现
  419. */
  420. _ondragend: function (param) {
  421. callChartListMethodReverse(this, 'ondragend', param, this._status);
  422. this._timeline && this._timeline.ondragend(param, this._status);
  423. this._island.ondragend(param, this._status);
  424. // 发生过重计算
  425. if (this._status.needRefresh) {
  426. this._syncBackupData(this._option);
  427. var messageCenter = this._messageCenter;
  428. messageCenter.dispatch(
  429. ecConfig.EVENT.DATA_CHANGED,
  430. param.event,
  431. this._eventPackage(param.target),
  432. this
  433. );
  434. messageCenter.dispatch(ecConfig.EVENT.REFRESH, null, null, this);
  435. }
  436. },
  437. /**
  438. * 图例选择响应
  439. */
  440. _onlegendSelected: function (param) {
  441. // 用于图表间通信
  442. this._status.needRefresh = false;
  443. callChartListMethodReverse(this, 'onlegendSelected', param, this._status);
  444. if (this._status.needRefresh) {
  445. this._messageCenter.dispatch(ecConfig.EVENT.REFRESH, null, null, this);
  446. }
  447. },
  448. /**
  449. * 数据区域缩放响应
  450. */
  451. _ondataZoom: function (param) {
  452. // 用于图表间通信
  453. this._status.needRefresh = false;
  454. callChartListMethodReverse(this, 'ondataZoom', param, this._status);
  455. if (this._status.needRefresh) {
  456. this._messageCenter.dispatch(ecConfig.EVENT.REFRESH, null, null, this);
  457. }
  458. },
  459. /**
  460. * 值域漫游响应
  461. */
  462. _ondataRange: function (param) {
  463. this._clearEffect();
  464. // 用于图表间通信
  465. this._status.needRefresh = false;
  466. callChartListMethodReverse(this, 'ondataRange', param, this._status);
  467. // 没有相互影响,直接刷新即可
  468. if (this._status.needRefresh) {
  469. this._zr.refreshNextFrame();
  470. }
  471. },
  472. /**
  473. * 动态类型切换响应
  474. */
  475. _onmagicTypeChanged: function () {
  476. this._clearEffect();
  477. this._render(this._toolbox.getMagicOption());
  478. },
  479. /**
  480. * 数据视图修改响应
  481. */
  482. _ondataViewChanged: function (param) {
  483. this._syncBackupData(param.option);
  484. this._messageCenter.dispatch(
  485. ecConfig.EVENT.DATA_CHANGED,
  486. null,
  487. param,
  488. this
  489. );
  490. this._messageCenter.dispatch(ecConfig.EVENT.REFRESH, null, null, this);
  491. },
  492. /**
  493. * tooltip与图表间通信
  494. */
  495. _tooltipHover: function (param) {
  496. var tipShape = [];
  497. callChartListMethodReverse(this, 'ontooltipHover', param, tipShape);
  498. },
  499. /**
  500. * 还原
  501. */
  502. _onrestore: function () {
  503. this.restore();
  504. },
  505. /**
  506. * 刷新
  507. */
  508. _onrefresh: function (param) {
  509. this._refreshInside = true;
  510. this.refresh(param);
  511. this._refreshInside = false;
  512. },
  513. /**
  514. * 数据修改后的反向同步dataZoom持有的备份数据
  515. */
  516. _syncBackupData: function (curOption) {
  517. this.component.dataZoom && this.component.dataZoom.syncBackupData(curOption);
  518. },
  519. /**
  520. * 打包Echarts层的事件附件
  521. */
  522. _eventPackage: function (target) {
  523. if (target) {
  524. var ecData = require('./util/ecData');
  525. var seriesIndex = ecData.get(target, 'seriesIndex');
  526. var dataIndex = ecData.get(target, 'dataIndex');
  527. dataIndex = seriesIndex != -1 && this.component.dataZoom
  528. ? this.component.dataZoom.getRealDataIndex(
  529. seriesIndex,
  530. dataIndex
  531. )
  532. : dataIndex;
  533. return {
  534. seriesIndex: seriesIndex,
  535. seriesName: (ecData.get(target, 'series') || {}).name,
  536. dataIndex: dataIndex,
  537. data: ecData.get(target, 'data'),
  538. name: ecData.get(target, 'name'),
  539. value: ecData.get(target, 'value'),
  540. special: ecData.get(target, 'special')
  541. };
  542. }
  543. return;
  544. },
  545. _noDataCheck: function(magicOption) {
  546. var series = magicOption.series;
  547. for (var i = 0, l = series.length; i < l; i++) {
  548. if (series[i].type == ecConfig.CHART_TYPE_MAP
  549. || (series[i].data && series[i].data.length > 0)
  550. || (series[i].markPoint && series[i].markPoint.data && series[i].markPoint.data.length > 0)
  551. || (series[i].markLine && series[i].markLine.data && series[i].markLine.data.length > 0)
  552. || (series[i].nodes && series[i].nodes.length > 0)
  553. || (series[i].links && series[i].links.length > 0)
  554. || (series[i].matrix && series[i].matrix.length > 0)
  555. || (series[i].eventList && series[i].eventList.length > 0)
  556. ) {
  557. return false; // 存在任意数据则为非空数据
  558. }
  559. }
  560. var loadOption = (this._option && this._option.noDataLoadingOption)
  561. || this._themeConfig.noDataLoadingOption
  562. || ecConfig.noDataLoadingOption
  563. || {
  564. text: (this._option && this._option.noDataText)
  565. || this._themeConfig.noDataText
  566. || ecConfig.noDataText,
  567. effect: (this._option && this._option.noDataEffect)
  568. || this._themeConfig.noDataEffect
  569. || ecConfig.noDataEffect
  570. };
  571. // 空数据
  572. this.clear();
  573. this.showLoading(loadOption);
  574. return true;
  575. },
  576. /**
  577. * 图表渲染
  578. */
  579. _render: function (magicOption) {
  580. this._mergeGlobalConifg(magicOption);
  581. if (this._noDataCheck(magicOption)) {
  582. return;
  583. }
  584. var bgColor = magicOption.backgroundColor;
  585. if (bgColor) {
  586. if (!_canvasSupported
  587. && bgColor.indexOf('rgba') != -1
  588. ) {
  589. // IE6~8对RGBA的处理,filter会带来其他颜色的影响
  590. var cList = bgColor.split(',');
  591. this.dom.style.filter = 'alpha(opacity=' +
  592. cList[3].substring(0, cList[3].lastIndexOf(')')) * 100
  593. + ')';
  594. cList.length = 3;
  595. cList[0] = cList[0].replace('a', '');
  596. this.dom.style.backgroundColor = cList.join(',') + ')';
  597. }
  598. else {
  599. this.dom.style.backgroundColor = bgColor;
  600. }
  601. }
  602. this._zr.clearAnimation();
  603. this._chartList = [];
  604. var chartLibrary = require('./chart');
  605. var componentLibrary = require('./component');
  606. if (magicOption.xAxis || magicOption.yAxis) {
  607. magicOption.grid = magicOption.grid || {};
  608. magicOption.dataZoom = magicOption.dataZoom || {};
  609. }
  610. var componentList = [
  611. 'title', 'legend', 'tooltip', 'dataRange', 'roamController',
  612. 'grid', 'dataZoom', 'xAxis', 'yAxis', 'polar'
  613. ];
  614. var ComponentClass;
  615. var componentType;
  616. var component;
  617. for (var i = 0, l = componentList.length; i < l; i++) {
  618. componentType = componentList[i];
  619. component = this.component[componentType];
  620. if (magicOption[componentType]) {
  621. if (component) {
  622. component.refresh && component.refresh(magicOption);
  623. }
  624. else {
  625. ComponentClass = componentLibrary.get(
  626. /^[xy]Axis$/.test(componentType) ? 'axis' : componentType
  627. );
  628. component = new ComponentClass(
  629. this._themeConfig, this._messageCenter, this._zr,
  630. magicOption, this, componentType
  631. );
  632. this.component[componentType] = component;
  633. }
  634. this._chartList.push(component);
  635. }
  636. else if (component) {
  637. component.dispose();
  638. this.component[componentType] = null;
  639. delete this.component[componentType];
  640. }
  641. }
  642. var ChartClass;
  643. var chartType;
  644. var chart;
  645. var chartMap = {}; // 记录已经初始化的图表
  646. for (var i = 0, l = magicOption.series.length; i < l; i++) {
  647. chartType = magicOption.series[i].type;
  648. if (!chartType) {
  649. console.error('series[' + i + '] chart type has not been defined.');
  650. continue;
  651. }
  652. if (!chartMap[chartType]) {
  653. chartMap[chartType] = true;
  654. ChartClass = chartLibrary.get(chartType);
  655. if (ChartClass) {
  656. if (this.chart[chartType]) {
  657. chart = this.chart[chartType];
  658. chart.refresh(magicOption);
  659. }
  660. else {
  661. chart = new ChartClass(
  662. this._themeConfig, this._messageCenter, this._zr,
  663. magicOption, this
  664. );
  665. }
  666. this._chartList.push(chart);
  667. this.chart[chartType] = chart;
  668. }
  669. else {
  670. console.error(chartType + ' has not been required.');
  671. }
  672. }
  673. }
  674. // 已有实例但新option不带这类图表的实例释放
  675. for (chartType in this.chart) {
  676. if (chartType != ecConfig.CHART_TYPE_ISLAND && !chartMap[chartType]) {
  677. this.chart[chartType].dispose();
  678. this.chart[chartType] = null;
  679. delete this.chart[chartType];
  680. }
  681. }
  682. this.component.grid && this.component.grid.refixAxisShape(this.component);
  683. this._island.refresh(magicOption);
  684. this._toolbox.refresh(magicOption);
  685. magicOption.animation && !magicOption.renderAsImage
  686. ? this._zr.refresh()
  687. : this._zr.render();
  688. var imgId = 'IMG' + this.id;
  689. var img = document.getElementById(imgId);
  690. if (magicOption.renderAsImage && _canvasSupported) {
  691. // IE8- 不支持图片渲染形式
  692. if (img) {
  693. // 已经渲染过则更新显示
  694. img.src = this.getDataURL(magicOption.renderAsImage);
  695. }
  696. else {
  697. // 没有渲染过插入img dom
  698. img = this.getImage(magicOption.renderAsImage);
  699. img.id = imgId;
  700. img.style.position = 'absolute';
  701. img.style.left = 0;
  702. img.style.top = 0;
  703. this.dom.firstChild.appendChild(img);
  704. }
  705. this.un();
  706. this._zr.un();
  707. this._disposeChartList();
  708. this._zr.clear();
  709. }
  710. else if (img) {
  711. // 删除可能存在的img
  712. img.parentNode.removeChild(img);
  713. }
  714. img = null;
  715. this._option = magicOption;
  716. },
  717. /**
  718. * 还原
  719. */
  720. restore: function () {
  721. this._clearEffect();
  722. this._option = zrUtil.clone(this._optionRestore);
  723. this._disposeChartList();
  724. this._island.clear();
  725. this._toolbox.reset(this._option, true);
  726. this._render(this._option);
  727. },
  728. /**
  729. * 刷新
  730. * @param {Object=} param可选参数用于附带option内部同步用外部不建议带入数据修改无法同步
  731. */
  732. refresh: function (param) {
  733. this._clearEffect();
  734. param = param || {};
  735. var magicOption = param.option;
  736. // 外部调用的refresh且有option带入
  737. if (!this._refreshInside && magicOption) {
  738. // 做简单的差异合并去同步内部持有的数据克隆,不建议带入数据
  739. // 开启数据区域缩放、拖拽重计算、数据视图可编辑模式情况下,当用户产生了数据变化后无法同步
  740. // 如有带入option存在数据变化,请重新setOption
  741. magicOption = this.getOption();
  742. zrUtil.merge(magicOption, param.option, true);
  743. zrUtil.merge(this._optionRestore, param.option, true);
  744. this._toolbox.reset(magicOption);
  745. }
  746. this._island.refresh(magicOption);
  747. this._toolbox.refresh(magicOption);
  748. // 停止动画
  749. this._zr.clearAnimation();
  750. // 先来后到,安顺序刷新各种图表,图表内部refresh优化检查magicOption,无需更新则不更新~
  751. for (var i = 0, l = this._chartList.length; i < l; i++) {
  752. this._chartList[i].refresh && this._chartList[i].refresh(magicOption);
  753. }
  754. this.component.grid && this.component.grid.refixAxisShape(this.component);
  755. this._zr.refresh();
  756. },
  757. /**
  758. * 释放图表实例
  759. */
  760. _disposeChartList: function () {
  761. this._clearEffect();
  762. // 停止动画
  763. this._zr.clearAnimation();
  764. var len = this._chartList.length;
  765. while (len--) {
  766. var chart = this._chartList[len];
  767. if (chart) {
  768. var chartType = chart.type;
  769. this.chart[chartType] && delete this.chart[chartType];
  770. this.component[chartType] && delete this.component[chartType];
  771. chart.dispose && chart.dispose();
  772. }
  773. }
  774. this._chartList = [];
  775. },
  776. /**
  777. * 非图表全局属性merge~~
  778. */
  779. _mergeGlobalConifg: function (magicOption) {
  780. var mergeList = [
  781. // 背景颜色
  782. 'backgroundColor',
  783. // 拖拽重计算相关
  784. 'calculable', 'calculableColor', 'calculableHolderColor',
  785. // 孤岛显示连接符
  786. 'nameConnector', 'valueConnector',
  787. // 动画相关
  788. 'animation', 'animationThreshold',
  789. 'animationDuration', 'animationDurationUpdate',
  790. 'animationEasing', 'addDataAnimation',
  791. // 默认标志图形类型列表
  792. 'symbolList',
  793. // 降低图表内元素拖拽敏感度,单位ms,不建议外部干预
  794. 'DRAG_ENABLE_TIME'
  795. ];
  796. var len = mergeList.length;
  797. while (len--) {
  798. var mergeItem = mergeList[len];
  799. if (magicOption[mergeItem] == null) {
  800. magicOption[mergeItem] = this._themeConfig[mergeItem] != null
  801. ? this._themeConfig[mergeItem]
  802. : ecConfig[mergeItem];
  803. }
  804. }
  805. // 数值系列的颜色列表,不传则采用内置颜色,可配数组,借用zrender实例注入,会有冲突风险,先这样
  806. var themeColor = magicOption.color;
  807. if (!(themeColor && themeColor.length)) {
  808. themeColor = this._themeConfig.color || ecConfig.color;
  809. }
  810. this._zr.getColor = function (idx) {
  811. var zrColor = require('zrender/tool/color');
  812. return zrColor.getColor(idx, themeColor);
  813. };
  814. if (!_canvasSupported) {
  815. // 不支持Canvas的强制关闭动画
  816. magicOption.animation = false;
  817. magicOption.addDataAnimation = false;
  818. }
  819. },
  820. /**
  821. * 万能接口配置图表实例任何可配置选项多次调用时option选项做merge处理
  822. * @param {Object} option
  823. * @param {boolean=} notMerge 多次调用时option选项是默认是合并merge
  824. * 如果不需求可以通过notMerger参数为true阻止与上次option的合并
  825. */
  826. setOption: function (option, notMerge) {
  827. if (!option.timeline) {
  828. return this._setOption(option, notMerge);
  829. }
  830. else {
  831. return this._setTimelineOption(option);
  832. }
  833. },
  834. /**
  835. * 万能接口配置图表实例任何可配置选项多次调用时option选项做merge处理
  836. * @param {Object} option
  837. * @param {boolean=} notMerge 多次调用时option选项是默认是合并merge
  838. * 如果不需求可以通过notMerger参数为true阻止与上次option的合并
  839. * @param {boolean=} 默认falsekeepTimeLine 表示从timeline组件调用而来
  840. * 表示当前行为是timeline的数据切换保持timeline
  841. * 反之销毁timeline 详见Issue #1601
  842. */
  843. _setOption: function (option, notMerge, keepTimeLine) {
  844. if (!notMerge && this._option) {
  845. this._option = zrUtil.merge(
  846. this.getOption(),
  847. zrUtil.clone(option),
  848. true
  849. );
  850. }
  851. else {
  852. this._option = zrUtil.clone(option);
  853. !keepTimeLine && this._timeline && this._timeline.dispose();
  854. }
  855. this._optionRestore = zrUtil.clone(this._option);
  856. if (!this._option.series || this._option.series.length === 0) {
  857. this._zr.clear();
  858. return;
  859. }
  860. if (this.component.dataZoom // 存在dataZoom控件
  861. && (this._option.dataZoom // 并且新option也存在
  862. || (this._option.toolbox
  863. && this._option.toolbox.feature
  864. && this._option.toolbox.feature.dataZoom
  865. && this._option.toolbox.feature.dataZoom.show
  866. )
  867. )
  868. ) {
  869. // dataZoom同步数据
  870. this.component.dataZoom.syncOption(this._option);
  871. }
  872. this._toolbox.reset(this._option);
  873. this._render(this._option);
  874. return this;
  875. },
  876. /**
  877. * 返回内部持有的当前显示option克隆
  878. */
  879. getOption: function () {
  880. var magicOption = zrUtil.clone(this._option);
  881. var self = this;
  882. function restoreOption(prop) {
  883. var restoreSource = self._optionRestore[prop];
  884. if (restoreSource) {
  885. if (restoreSource instanceof Array) {
  886. var len = restoreSource.length;
  887. while (len--) {
  888. magicOption[prop][len].data = zrUtil.clone(
  889. restoreSource[len].data
  890. );
  891. }
  892. }
  893. else {
  894. magicOption[prop].data = zrUtil.clone(restoreSource.data);
  895. }
  896. }
  897. }
  898. // 横轴数据还原
  899. restoreOption('xAxis');
  900. // 纵轴数据还原
  901. restoreOption('yAxis');
  902. // 系列数据还原
  903. restoreOption('series');
  904. return magicOption;
  905. },
  906. /**
  907. * 数据设置快捷接口
  908. * @param {Array} series
  909. * @param {boolean=} notMerge 多次调用时option选项是默认是合并merge
  910. * 如果不需求可以通过notMerger参数为true阻止与上次option的合并
  911. */
  912. setSeries: function (series, notMerge) {
  913. if (!notMerge) {
  914. this.setOption({series: series});
  915. }
  916. else {
  917. this._option.series = series;
  918. this.setOption(this._option, notMerge);
  919. }
  920. return this;
  921. },
  922. /**
  923. * 返回内部持有的当前显示series克隆
  924. */
  925. getSeries: function () {
  926. return this.getOption().series;
  927. },
  928. /**
  929. * timelineOption接口配置图表实例任何可配置选项
  930. * @param {Object} option
  931. */
  932. _setTimelineOption: function(option) {
  933. this._timeline && this._timeline.dispose();
  934. var Timeline = require('./component/timeline');
  935. var timeline = new Timeline(
  936. this._themeConfig, this._messageCenter, this._zr, option, this
  937. );
  938. this._timeline = timeline;
  939. this.component.timeline = this._timeline;
  940. return this;
  941. },
  942. /**
  943. * 动态数据添加
  944. * 形参为单组数据参数多组时为数据内容同[seriesIdx, data, isShift, additionData]
  945. * @param {number} seriesIdx 系列索引
  946. * @param {number | Object} data 增加数据
  947. * @param {boolean=} isHead 是否队头加入默认不指定或false时为队尾插入
  948. * @param {boolean=} dataGrow 是否增长数据队列长度默认不指定或false时移出目标数组对位数据
  949. * @param {string=} additionData 是否增加类目轴(饼图为图例)数据附加操作同isHead和dataGrow
  950. */
  951. addData: function (seriesIdx, data, isHead, dataGrow, additionData) {
  952. var params = seriesIdx instanceof Array
  953. ? seriesIdx
  954. : [[seriesIdx, data, isHead, dataGrow, additionData]];
  955. //this._optionRestore 和 magicOption 都要同步
  956. var magicOption = this.getOption();
  957. var optionRestore = this._optionRestore;
  958. var self = this;
  959. for (var i = 0, l = params.length; i < l; i++) {
  960. seriesIdx = params[i][0];
  961. data = params[i][1];
  962. isHead = params[i][2];
  963. dataGrow = params[i][3];
  964. additionData = params[i][4];
  965. var seriesItem = optionRestore.series[seriesIdx];
  966. var inMethod = isHead ? 'unshift' : 'push';
  967. var outMethod = isHead ? 'pop' : 'shift';
  968. if (seriesItem) {
  969. var seriesItemData = seriesItem.data;
  970. var mSeriesItemData = magicOption.series[seriesIdx].data;
  971. seriesItemData[inMethod](data);
  972. mSeriesItemData[inMethod](data);
  973. if (!dataGrow) {
  974. seriesItemData[outMethod]();
  975. data = mSeriesItemData[outMethod]();
  976. }
  977. if (additionData != null) {
  978. var legend;
  979. var legendData;
  980. if (seriesItem.type === ecConfig.CHART_TYPE_PIE
  981. && (legend = optionRestore.legend)
  982. && (legendData = legend.data)
  983. ) {
  984. var mLegendData = magicOption.legend.data;
  985. legendData[inMethod](additionData);
  986. mLegendData[inMethod](additionData);
  987. if (!dataGrow) {
  988. var legendDataIdx = zrUtil.indexOf(legendData, data.name);
  989. legendDataIdx != -1 && legendData.splice(legendDataIdx, 1);
  990. legendDataIdx = zrUtil.indexOf(mLegendData, data.name);
  991. legendDataIdx != -1 && mLegendData.splice(legendDataIdx, 1);
  992. }
  993. }
  994. else if (optionRestore.xAxis != null && optionRestore.yAxis != null) {
  995. // x轴类目
  996. var axisData;
  997. var mAxisData;
  998. var axisIdx = seriesItem.xAxisIndex || 0;
  999. if (optionRestore.xAxis[axisIdx].type == null
  1000. || optionRestore.xAxis[axisIdx].type === 'category'
  1001. ) {
  1002. axisData = optionRestore.xAxis[axisIdx].data;
  1003. mAxisData = magicOption.xAxis[axisIdx].data;
  1004. axisData[inMethod](additionData);
  1005. mAxisData[inMethod](additionData);
  1006. if (!dataGrow) {
  1007. axisData[outMethod]();
  1008. mAxisData[outMethod]();
  1009. }
  1010. }
  1011. // y轴类目
  1012. axisIdx = seriesItem.yAxisIndex || 0;
  1013. if (optionRestore.yAxis[axisIdx].type === 'category') {
  1014. axisData = optionRestore.yAxis[axisIdx].data;
  1015. mAxisData = magicOption.yAxis[axisIdx].data;
  1016. axisData[inMethod](additionData);
  1017. mAxisData[inMethod](additionData);
  1018. if (!dataGrow) {
  1019. axisData[outMethod]();
  1020. mAxisData[outMethod]();
  1021. }
  1022. }
  1023. }
  1024. }
  1025. // 同步图表内状态,动画需要
  1026. this._option.series[seriesIdx].data = magicOption.series[seriesIdx].data;
  1027. }
  1028. }
  1029. this._zr.clearAnimation();
  1030. var chartList = this._chartList;
  1031. var chartAnimationCount = 0;
  1032. var chartAnimationDone = function () {
  1033. chartAnimationCount--;
  1034. if (chartAnimationCount === 0) {
  1035. animationDone();
  1036. }
  1037. };
  1038. for (var i = 0, l = chartList.length; i < l; i++) {
  1039. if (magicOption.addDataAnimation && chartList[i].addDataAnimation) {
  1040. chartAnimationCount++;
  1041. chartList[i].addDataAnimation(params, chartAnimationDone);
  1042. }
  1043. }
  1044. // dataZoom同步数据
  1045. this.component.dataZoom && this.component.dataZoom.syncOption(magicOption);
  1046. this._option = magicOption;
  1047. function animationDone() {
  1048. if (!self._zr) {
  1049. return; // 已经被释放
  1050. }
  1051. self._zr.clearAnimation();
  1052. for (var i = 0, l = chartList.length; i < l; i++) {
  1053. // 有addData动画就去掉过渡动画
  1054. chartList[i].motionlessOnce =
  1055. magicOption.addDataAnimation && chartList[i].addDataAnimation;
  1056. }
  1057. self._messageCenter.dispatch(
  1058. ecConfig.EVENT.REFRESH,
  1059. null,
  1060. {option: magicOption},
  1061. self
  1062. );
  1063. }
  1064. if (!magicOption.addDataAnimation) {
  1065. setTimeout(animationDone, 0);
  1066. }
  1067. return this;
  1068. },
  1069. /**
  1070. * 动态[标注 | 标线]添加
  1071. * @param {number} seriesIdx 系列索引
  1072. * @param {Object} markData [标注 | 标线]对象支持多个
  1073. */
  1074. addMarkPoint: function (seriesIdx, markData) {
  1075. return this._addMark(seriesIdx, markData, 'markPoint');
  1076. },
  1077. addMarkLine: function (seriesIdx, markData) {
  1078. return this._addMark(seriesIdx, markData, 'markLine');
  1079. },
  1080. _addMark: function (seriesIdx, markData, markType) {
  1081. var series = this._option.series;
  1082. var seriesItem;
  1083. if (series && (seriesItem = series[seriesIdx])) {
  1084. var seriesR = this._optionRestore.series;
  1085. var seriesRItem = seriesR[seriesIdx];
  1086. var markOpt = seriesItem[markType];
  1087. var markOptR = seriesRItem[markType];
  1088. markOpt = seriesItem[markType] = markOpt || {data: []};
  1089. markOptR = seriesRItem[markType] = markOptR || {data: []};
  1090. for (var key in markData) {
  1091. if (key === 'data') {
  1092. // 数据concat
  1093. markOpt.data = markOpt.data.concat(markData.data);
  1094. markOptR.data = markOptR.data.concat(markData.data);
  1095. }
  1096. else if (typeof markData[key] != 'object' || markOpt[key] == null) {
  1097. // 简单类型或新值直接赋值
  1098. markOpt[key] = markOptR[key] = markData[key];
  1099. }
  1100. else {
  1101. // 非数据的复杂对象merge
  1102. zrUtil.merge(markOpt[key], markData[key], true);
  1103. zrUtil.merge(markOptR[key], markData[key], true);
  1104. }
  1105. }
  1106. var chart = this.chart[seriesItem.type];
  1107. chart && chart.addMark(seriesIdx, markData, markType);
  1108. }
  1109. return this;
  1110. },
  1111. /**
  1112. * 动态[标注 | 标线]删除
  1113. * @param {number} seriesIdx 系列索引
  1114. * @param {string} markName [标注 | 标线]名称
  1115. */
  1116. delMarkPoint: function (seriesIdx, markName) {
  1117. return this._delMark(seriesIdx, markName, 'markPoint');
  1118. },
  1119. delMarkLine: function (seriesIdx, markName) {
  1120. return this._delMark(seriesIdx, markName, 'markLine');
  1121. },
  1122. _delMark: function (seriesIdx, markName, markType) {
  1123. var series = this._option.series;
  1124. var seriesItem;
  1125. var mark;
  1126. var dataArray;
  1127. if (!(
  1128. series
  1129. && (seriesItem = series[seriesIdx])
  1130. && (mark = seriesItem[markType])
  1131. && (dataArray = mark.data)
  1132. )
  1133. ) {
  1134. return this;
  1135. }
  1136. markName = markName.split(' > ');
  1137. var targetIndex = -1;
  1138. for (var i = 0, l = dataArray.length; i < l; i++) {
  1139. var dataItem = dataArray[i];
  1140. if (dataItem instanceof Array) {
  1141. if (dataItem[0].name === markName[0]
  1142. && dataItem[1].name === markName[1]
  1143. ) {
  1144. targetIndex = i;
  1145. break;
  1146. }
  1147. }
  1148. else if (dataItem.name === markName[0]) {
  1149. targetIndex = i;
  1150. break;
  1151. }
  1152. }
  1153. if (targetIndex > -1) {
  1154. dataArray.splice(targetIndex, 1);
  1155. this._optionRestore.series[seriesIdx][markType].data.splice(targetIndex, 1);
  1156. var chart = this.chart[seriesItem.type];
  1157. chart && chart.delMark(seriesIdx, markName.join(' > '), markType);
  1158. }
  1159. return this;
  1160. },
  1161. /**
  1162. * 获取当前dom
  1163. */
  1164. getDom: function () {
  1165. return this.dom;
  1166. },
  1167. /**
  1168. * 获取当前zrender实例可用于添加额为的shape和深度控制
  1169. */
  1170. getZrender: function () {
  1171. return this._zr;
  1172. },
  1173. /**
  1174. * 获取Base64图片dataURL
  1175. * @param {string} imgType 图片类型支持png|jpeg默认为png
  1176. * @return imgDataURL
  1177. */
  1178. getDataURL: function (imgType) {
  1179. if (!_canvasSupported) {
  1180. return '';
  1181. }
  1182. if (this._chartList.length === 0) {
  1183. // 渲染为图片
  1184. var imgId = 'IMG' + this.id;
  1185. var img = document.getElementById(imgId);
  1186. if (img) {
  1187. return img.src;
  1188. }
  1189. }
  1190. // 清除可能存在的tooltip元素
  1191. var tooltip = this.component.tooltip;
  1192. tooltip && tooltip.hideTip();
  1193. switch (imgType) {
  1194. case 'jpeg':
  1195. break;
  1196. default:
  1197. imgType = 'png';
  1198. }
  1199. var bgColor = this._option.backgroundColor;
  1200. if (bgColor && bgColor.replace(' ','') === 'rgba(0,0,0,0)') {
  1201. bgColor = '#fff';
  1202. }
  1203. return this._zr.toDataURL('image/' + imgType, bgColor);
  1204. },
  1205. /**
  1206. * 获取img
  1207. * @param {string} imgType 图片类型支持png|jpeg默认为png
  1208. * @return img dom
  1209. */
  1210. getImage: function (imgType) {
  1211. var title = this._optionRestore.title;
  1212. var imgDom = document.createElement('img');
  1213. imgDom.src = this.getDataURL(imgType);
  1214. imgDom.title = (title && title.text) || 'ECharts';
  1215. return imgDom;
  1216. },
  1217. /**
  1218. * 获取多图联动的Base64图片dataURL
  1219. * @param {string} imgType 图片类型支持png|jpeg默认为png
  1220. * @return imgDataURL
  1221. */
  1222. getConnectedDataURL: function (imgType) {
  1223. if (!this.isConnected()) {
  1224. return this.getDataURL(imgType);
  1225. }
  1226. var tempDom = this.dom;
  1227. var imgList = {
  1228. 'self': {
  1229. img: this.getDataURL(imgType),
  1230. left: tempDom.offsetLeft,
  1231. top: tempDom.offsetTop,
  1232. right: tempDom.offsetLeft + tempDom.offsetWidth,
  1233. bottom: tempDom.offsetTop + tempDom.offsetHeight
  1234. }
  1235. };
  1236. var minLeft = imgList.self.left;
  1237. var minTop = imgList.self.top;
  1238. var maxRight = imgList.self.right;
  1239. var maxBottom = imgList.self.bottom;
  1240. for (var c in this._connected) {
  1241. tempDom = this._connected[c].getDom();
  1242. imgList[c] = {
  1243. img: this._connected[c].getDataURL(imgType),
  1244. left: tempDom.offsetLeft,
  1245. top: tempDom.offsetTop,
  1246. right: tempDom.offsetLeft + tempDom.offsetWidth,
  1247. bottom: tempDom.offsetTop + tempDom.offsetHeight
  1248. };
  1249. minLeft = Math.min(minLeft, imgList[c].left);
  1250. minTop = Math.min(minTop, imgList[c].top);
  1251. maxRight = Math.max(maxRight, imgList[c].right);
  1252. maxBottom = Math.max(maxBottom, imgList[c].bottom);
  1253. }
  1254. var zrDom = document.createElement('div');
  1255. zrDom.style.position = 'absolute';
  1256. zrDom.style.left = '-4000px';
  1257. zrDom.style.width = (maxRight - minLeft) + 'px';
  1258. zrDom.style.height = (maxBottom - minTop) + 'px';
  1259. document.body.appendChild(zrDom);
  1260. var zrImg = require('zrender').init(zrDom);
  1261. var ImageShape = require('zrender/shape/Image');
  1262. for (var c in imgList) {
  1263. zrImg.addShape(new ImageShape({
  1264. style: {
  1265. x: imgList[c].left - minLeft,
  1266. y: imgList[c].top - minTop,
  1267. image: imgList[c].img
  1268. }
  1269. }));
  1270. }
  1271. zrImg.render();
  1272. var bgColor = this._option.backgroundColor;
  1273. if (bgColor && bgColor.replace(/ /g, '') === 'rgba(0,0,0,0)') {
  1274. bgColor = '#fff';
  1275. }
  1276. var image = zrImg.toDataURL('image/png', bgColor);
  1277. setTimeout(function () {
  1278. zrImg.dispose();
  1279. zrDom.parentNode.removeChild(zrDom);
  1280. zrDom = null;
  1281. }, 100);
  1282. return image;
  1283. },
  1284. /**
  1285. * 获取多图联动的img
  1286. * @param {string} imgType 图片类型支持png|jpeg默认为png
  1287. * @return img dom
  1288. */
  1289. getConnectedImage: function (imgType) {
  1290. var title = this._optionRestore.title;
  1291. var imgDom = document.createElement('img');
  1292. imgDom.src = this.getConnectedDataURL(imgType);
  1293. imgDom.title = (title && title.text) || 'ECharts';
  1294. return imgDom;
  1295. },
  1296. /**
  1297. * 外部接口绑定事件
  1298. * @param {Object} eventName 事件名称
  1299. * @param {Object} eventListener 事件响应函数
  1300. */
  1301. on: function (eventName, eventListener) {
  1302. this._messageCenterOutSide.bind(eventName, eventListener, this);
  1303. return this;
  1304. },
  1305. /**
  1306. * 外部接口解除事件绑定
  1307. * @param {Object} eventName 事件名称
  1308. * @param {Object} eventListener 事件响应函数
  1309. */
  1310. un: function (eventName, eventListener) {
  1311. this._messageCenterOutSide.unbind(eventName, eventListener);
  1312. return this;
  1313. },
  1314. /**
  1315. * 多图联动
  1316. * @param connectTarget{ECharts | Array <ECharts>} connectTarget 联动目标
  1317. */
  1318. connect: function (connectTarget) {
  1319. if (!connectTarget) {
  1320. return this;
  1321. }
  1322. if (!this._connected) {
  1323. this._connected = {};
  1324. }
  1325. if (connectTarget instanceof Array) {
  1326. for (var i = 0, l = connectTarget.length; i < l; i++) {
  1327. this._connected[connectTarget[i].id] = connectTarget[i];
  1328. }
  1329. }
  1330. else {
  1331. this._connected[connectTarget.id] = connectTarget;
  1332. }
  1333. return this;
  1334. },
  1335. /**
  1336. * 解除多图联动
  1337. * @param connectTarget{ECharts | Array <ECharts>} connectTarget 解除联动目标
  1338. */
  1339. disConnect: function (connectTarget) {
  1340. if (!connectTarget || !this._connected) {
  1341. return this;
  1342. }
  1343. if (connectTarget instanceof Array) {
  1344. for (var i = 0, l = connectTarget.length; i < l; i++) {
  1345. delete this._connected[connectTarget[i].id];
  1346. }
  1347. }
  1348. else {
  1349. delete this._connected[connectTarget.id];
  1350. }
  1351. for (var k in this._connected) {
  1352. return k, this; // 非空
  1353. }
  1354. // 空,转为标志位
  1355. this._connected = false;
  1356. return this;
  1357. },
  1358. /**
  1359. * 联动事件响应
  1360. */
  1361. connectedEventHandler: function (param) {
  1362. if (param.__echartsId != this.id) {
  1363. // 来自其他联动图表的事件
  1364. this._onevent(param);
  1365. }
  1366. },
  1367. /**
  1368. * 是否存在多图联动
  1369. */
  1370. isConnected: function () {
  1371. return !!this._connected;
  1372. },
  1373. /**
  1374. * 显示loading过渡
  1375. * @param {Object} loadingOption
  1376. */
  1377. showLoading: function (loadingOption) {
  1378. var effectList = {
  1379. bar: require('zrender/loadingEffect/Bar'),
  1380. bubble: require('zrender/loadingEffect/Bubble'),
  1381. dynamicLine: require('zrender/loadingEffect/DynamicLine'),
  1382. ring: require('zrender/loadingEffect/Ring'),
  1383. spin: require('zrender/loadingEffect/Spin'),
  1384. whirling: require('zrender/loadingEffect/Whirling')
  1385. };
  1386. this._toolbox.hideDataView();
  1387. loadingOption = loadingOption || {};
  1388. var textStyle = loadingOption.textStyle || {};
  1389. loadingOption.textStyle = textStyle;
  1390. var finalTextStyle = zrUtil.merge(
  1391. zrUtil.merge(
  1392. zrUtil.clone(textStyle),
  1393. this._themeConfig.textStyle
  1394. ),
  1395. ecConfig.textStyle
  1396. );
  1397. textStyle.textFont = finalTextStyle.fontStyle + ' '
  1398. + finalTextStyle.fontWeight + ' '
  1399. + finalTextStyle.fontSize + 'px '
  1400. + finalTextStyle.fontFamily;
  1401. textStyle.text = loadingOption.text
  1402. || (this._option && this._option.loadingText)
  1403. || this._themeConfig.loadingText
  1404. || ecConfig.loadingText;
  1405. if (loadingOption.x != null) {
  1406. textStyle.x = loadingOption.x;
  1407. }
  1408. if (loadingOption.y != null) {
  1409. textStyle.y = loadingOption.y;
  1410. }
  1411. loadingOption.effectOption = loadingOption.effectOption || {};
  1412. loadingOption.effectOption.textStyle = textStyle;
  1413. var Effect = loadingOption.effect;
  1414. if (typeof Effect === 'string' || Effect == null) {
  1415. Effect = effectList[
  1416. loadingOption.effect
  1417. || (this._option && this._option.loadingEffect)
  1418. || this._themeConfig.loadingEffect
  1419. || ecConfig.loadingEffect
  1420. ]
  1421. || effectList.spin;
  1422. }
  1423. this._zr.showLoading(new Effect(loadingOption.effectOption));
  1424. return this;
  1425. },
  1426. /**
  1427. * 隐藏loading过渡
  1428. */
  1429. hideLoading: function () {
  1430. this._zr.hideLoading();
  1431. return this;
  1432. },
  1433. /**
  1434. * 主题设置
  1435. */
  1436. setTheme: function (theme) {
  1437. if (theme) {
  1438. if (typeof theme === 'string') {
  1439. // 默认主题
  1440. switch (theme) {
  1441. case 'macarons':
  1442. theme = require('./theme/macarons');
  1443. break;
  1444. case 'infographic':
  1445. theme = require('./theme/infographic');
  1446. break;
  1447. default:
  1448. theme = {}; // require('./theme/default');
  1449. }
  1450. }
  1451. else {
  1452. theme = theme || {};
  1453. }
  1454. // // 复位默认配置
  1455. // // this._themeConfig会被别的对象引用持有
  1456. // // 所以不能改成this._themeConfig = {};
  1457. // for (var key in this._themeConfig) {
  1458. // delete this._themeConfig[key];
  1459. // }
  1460. // for (var key in ecConfig) {
  1461. // this._themeConfig[key] = zrUtil.clone(ecConfig[key]);
  1462. // }
  1463. // // 颜色数组随theme,不merge
  1464. // theme.color && (this._themeConfig.color = []);
  1465. // // 默认标志图形类型列表,不merge
  1466. // theme.symbolList && (this._themeConfig.symbolList = []);
  1467. // // 应用新主题
  1468. // zrUtil.merge(this._themeConfig, zrUtil.clone(theme), true);
  1469. this._themeConfig = theme;
  1470. }
  1471. if (!_canvasSupported) { // IE8-
  1472. var textStyle = this._themeConfig.textStyle;
  1473. textStyle && textStyle.fontFamily && textStyle.fontFamily2
  1474. && (textStyle.fontFamily = textStyle.fontFamily2);
  1475. textStyle = ecConfig.textStyle;
  1476. textStyle.fontFamily = textStyle.fontFamily2;
  1477. }
  1478. this._timeline && this._timeline.setTheme(true);
  1479. this._optionRestore && this.restore();
  1480. },
  1481. /**
  1482. * 视图区域大小变化更新不默认绑定供使用方按需调用
  1483. */
  1484. resize: function () {
  1485. var self = this;
  1486. return function(){
  1487. self._clearEffect();
  1488. self._zr.resize();
  1489. if (self._option && self._option.renderAsImage && _canvasSupported) {
  1490. // 渲染为图片重走render模式
  1491. self._render(self._option);
  1492. return self;
  1493. }
  1494. // 停止动画
  1495. self._zr.clearAnimation();
  1496. self._island.resize();
  1497. self._toolbox.resize();
  1498. self._timeline && self._timeline.resize();
  1499. // 先来后到,不能仅刷新自己,也不能在上一个循环中刷新,如坐标系数据改变会影响其他图表的大小
  1500. // 所以安顺序刷新各种图表,图表内部refresh优化无需更新则不更新~
  1501. for (var i = 0, l = self._chartList.length; i < l; i++) {
  1502. self._chartList[i].resize && self._chartList[i].resize();
  1503. }
  1504. self.component.grid && self.component.grid.refixAxisShape(self.component);
  1505. self._zr.refresh();
  1506. self._messageCenter.dispatch(ecConfig.EVENT.RESIZE, null, null, self);
  1507. return self;
  1508. };
  1509. },
  1510. _clearEffect: function() {
  1511. this._zr.modLayer(ecConfig.EFFECT_ZLEVEL, { motionBlur: false });
  1512. this._zr.painter.clearLayer(ecConfig.EFFECT_ZLEVEL);
  1513. },
  1514. /**
  1515. * 清除已渲染内容 clear后echarts实例可用
  1516. */
  1517. clear: function () {
  1518. this._disposeChartList();
  1519. this._zr.clear();
  1520. this._option = {};
  1521. this._optionRestore = {};
  1522. this.dom.style.backgroundColor = null;
  1523. return this;
  1524. },
  1525. /**
  1526. * 释放dispose后echarts实例不可用
  1527. */
  1528. dispose: function () {
  1529. var key = this.dom.getAttribute(DOM_ATTRIBUTE_KEY);
  1530. key && delete _instances[key];
  1531. this._island.dispose();
  1532. this._toolbox.dispose();
  1533. this._timeline && this._timeline.dispose();
  1534. this._messageCenter.unbind();
  1535. this.clear();
  1536. this._zr.dispose();
  1537. this._zr = null;
  1538. }
  1539. };
  1540. return self;
  1541. });