modelHelper.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. var zrUtil = require("zrender/lib/core/util");
  20. var Model = require("../../model/Model");
  21. /*
  22. * Licensed to the Apache Software Foundation (ASF) under one
  23. * or more contributor license agreements. See the NOTICE file
  24. * distributed with this work for additional information
  25. * regarding copyright ownership. The ASF licenses this file
  26. * to you under the Apache License, Version 2.0 (the
  27. * "License"); you may not use this file except in compliance
  28. * with the License. You may obtain a copy of the License at
  29. *
  30. * http://www.apache.org/licenses/LICENSE-2.0
  31. *
  32. * Unless required by applicable law or agreed to in writing,
  33. * software distributed under the License is distributed on an
  34. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  35. * KIND, either express or implied. See the License for the
  36. * specific language governing permissions and limitations
  37. * under the License.
  38. */
  39. var each = zrUtil.each;
  40. var curry = zrUtil.curry; // Build axisPointerModel, mergin tooltip.axisPointer model for each axis.
  41. // allAxesInfo should be updated when setOption performed.
  42. function collect(ecModel, api) {
  43. var result = {
  44. /**
  45. * key: makeKey(axis.model)
  46. * value: {
  47. * axis,
  48. * coordSys,
  49. * axisPointerModel,
  50. * triggerTooltip,
  51. * involveSeries,
  52. * snap,
  53. * seriesModels,
  54. * seriesDataCount
  55. * }
  56. */
  57. axesInfo: {},
  58. seriesInvolved: false,
  59. /**
  60. * key: makeKey(coordSys.model)
  61. * value: Object: key makeKey(axis.model), value: axisInfo
  62. */
  63. coordSysAxesInfo: {},
  64. coordSysMap: {}
  65. };
  66. collectAxesInfo(result, ecModel, api); // Check seriesInvolved for performance, in case too many series in some chart.
  67. result.seriesInvolved && collectSeriesInfo(result, ecModel);
  68. return result;
  69. }
  70. function collectAxesInfo(result, ecModel, api) {
  71. var globalTooltipModel = ecModel.getComponent('tooltip');
  72. var globalAxisPointerModel = ecModel.getComponent('axisPointer'); // links can only be set on global.
  73. var linksOption = globalAxisPointerModel.get('link', true) || [];
  74. var linkGroups = []; // Collect axes info.
  75. each(api.getCoordinateSystems(), function (coordSys) {
  76. // Some coordinate system do not support axes, like geo.
  77. if (!coordSys.axisPointerEnabled) {
  78. return;
  79. }
  80. var coordSysKey = makeKey(coordSys.model);
  81. var axesInfoInCoordSys = result.coordSysAxesInfo[coordSysKey] = {};
  82. result.coordSysMap[coordSysKey] = coordSys; // Set tooltip (like 'cross') is a convienent way to show axisPointer
  83. // for user. So we enable seting tooltip on coordSys model.
  84. var coordSysModel = coordSys.model;
  85. var baseTooltipModel = coordSysModel.getModel('tooltip', globalTooltipModel);
  86. each(coordSys.getAxes(), curry(saveTooltipAxisInfo, false, null)); // If axis tooltip used, choose tooltip axis for each coordSys.
  87. // Notice this case: coordSys is `grid` but not `cartesian2D` here.
  88. if (coordSys.getTooltipAxes && globalTooltipModel // If tooltip.showContent is set as false, tooltip will not
  89. // show but axisPointer will show as normal.
  90. && baseTooltipModel.get('show')) {
  91. // Compatible with previous logic. But series.tooltip.trigger: 'axis'
  92. // or series.data[n].tooltip.trigger: 'axis' are not support any more.
  93. var triggerAxis = baseTooltipModel.get('trigger') === 'axis';
  94. var cross = baseTooltipModel.get('axisPointer.type') === 'cross';
  95. var tooltipAxes = coordSys.getTooltipAxes(baseTooltipModel.get('axisPointer.axis'));
  96. if (triggerAxis || cross) {
  97. each(tooltipAxes.baseAxes, curry(saveTooltipAxisInfo, cross ? 'cross' : true, triggerAxis));
  98. }
  99. if (cross) {
  100. each(tooltipAxes.otherAxes, curry(saveTooltipAxisInfo, 'cross', false));
  101. }
  102. } // fromTooltip: true | false | 'cross'
  103. // triggerTooltip: true | false | null
  104. function saveTooltipAxisInfo(fromTooltip, triggerTooltip, axis) {
  105. var axisPointerModel = axis.model.getModel('axisPointer', globalAxisPointerModel);
  106. var axisPointerShow = axisPointerModel.get('show');
  107. if (!axisPointerShow || axisPointerShow === 'auto' && !fromTooltip && !isHandleTrigger(axisPointerModel)) {
  108. return;
  109. }
  110. if (triggerTooltip == null) {
  111. triggerTooltip = axisPointerModel.get('triggerTooltip');
  112. }
  113. axisPointerModel = fromTooltip ? makeAxisPointerModel(axis, baseTooltipModel, globalAxisPointerModel, ecModel, fromTooltip, triggerTooltip) : axisPointerModel;
  114. var snap = axisPointerModel.get('snap');
  115. var key = makeKey(axis.model);
  116. var involveSeries = triggerTooltip || snap || axis.type === 'category'; // If result.axesInfo[key] exist, override it (tooltip has higher priority).
  117. var axisInfo = result.axesInfo[key] = {
  118. key: key,
  119. axis: axis,
  120. coordSys: coordSys,
  121. axisPointerModel: axisPointerModel,
  122. triggerTooltip: triggerTooltip,
  123. involveSeries: involveSeries,
  124. snap: snap,
  125. useHandle: isHandleTrigger(axisPointerModel),
  126. seriesModels: []
  127. };
  128. axesInfoInCoordSys[key] = axisInfo;
  129. result.seriesInvolved |= involveSeries;
  130. var groupIndex = getLinkGroupIndex(linksOption, axis);
  131. if (groupIndex != null) {
  132. var linkGroup = linkGroups[groupIndex] || (linkGroups[groupIndex] = {
  133. axesInfo: {}
  134. });
  135. linkGroup.axesInfo[key] = axisInfo;
  136. linkGroup.mapper = linksOption[groupIndex].mapper;
  137. axisInfo.linkGroup = linkGroup;
  138. }
  139. }
  140. });
  141. }
  142. function makeAxisPointerModel(axis, baseTooltipModel, globalAxisPointerModel, ecModel, fromTooltip, triggerTooltip) {
  143. var tooltipAxisPointerModel = baseTooltipModel.getModel('axisPointer');
  144. var volatileOption = {};
  145. each(['type', 'snap', 'lineStyle', 'shadowStyle', 'label', 'animation', 'animationDurationUpdate', 'animationEasingUpdate', 'z'], function (field) {
  146. volatileOption[field] = zrUtil.clone(tooltipAxisPointerModel.get(field));
  147. }); // category axis do not auto snap, otherwise some tick that do not
  148. // has value can not be hovered. value/time/log axis default snap if
  149. // triggered from tooltip and trigger tooltip.
  150. volatileOption.snap = axis.type !== 'category' && !!triggerTooltip; // Compatibel with previous behavior, tooltip axis do not show label by default.
  151. // Only these properties can be overrided from tooltip to axisPointer.
  152. if (tooltipAxisPointerModel.get('type') === 'cross') {
  153. volatileOption.type = 'line';
  154. }
  155. var labelOption = volatileOption.label || (volatileOption.label = {}); // Follow the convention, do not show label when triggered by tooltip by default.
  156. labelOption.show == null && (labelOption.show = false);
  157. if (fromTooltip === 'cross') {
  158. // When 'cross', both axes show labels.
  159. var tooltipAxisPointerLabelShow = tooltipAxisPointerModel.get('label.show');
  160. labelOption.show = tooltipAxisPointerLabelShow != null ? tooltipAxisPointerLabelShow : true; // If triggerTooltip, this is a base axis, which should better not use cross style
  161. // (cross style is dashed by default)
  162. if (!triggerTooltip) {
  163. var crossStyle = volatileOption.lineStyle = tooltipAxisPointerModel.get('crossStyle');
  164. crossStyle && zrUtil.defaults(labelOption, crossStyle.textStyle);
  165. }
  166. }
  167. return axis.model.getModel('axisPointer', new Model(volatileOption, globalAxisPointerModel, ecModel));
  168. }
  169. function collectSeriesInfo(result, ecModel) {
  170. // Prepare data for axis trigger
  171. ecModel.eachSeries(function (seriesModel) {
  172. // Notice this case: this coordSys is `cartesian2D` but not `grid`.
  173. var coordSys = seriesModel.coordinateSystem;
  174. var seriesTooltipTrigger = seriesModel.get('tooltip.trigger', true);
  175. var seriesTooltipShow = seriesModel.get('tooltip.show', true);
  176. if (!coordSys || seriesTooltipTrigger === 'none' || seriesTooltipTrigger === false || seriesTooltipTrigger === 'item' || seriesTooltipShow === false || seriesModel.get('axisPointer.show', true) === false) {
  177. return;
  178. }
  179. each(result.coordSysAxesInfo[makeKey(coordSys.model)], function (axisInfo) {
  180. var axis = axisInfo.axis;
  181. if (coordSys.getAxis(axis.dim) === axis) {
  182. axisInfo.seriesModels.push(seriesModel);
  183. axisInfo.seriesDataCount == null && (axisInfo.seriesDataCount = 0);
  184. axisInfo.seriesDataCount += seriesModel.getData().count();
  185. }
  186. });
  187. }, this);
  188. }
  189. /**
  190. * For example:
  191. * {
  192. * axisPointer: {
  193. * links: [{
  194. * xAxisIndex: [2, 4],
  195. * yAxisIndex: 'all'
  196. * }, {
  197. * xAxisId: ['a5', 'a7'],
  198. * xAxisName: 'xxx'
  199. * }]
  200. * }
  201. * }
  202. */
  203. function getLinkGroupIndex(linksOption, axis) {
  204. var axisModel = axis.model;
  205. var dim = axis.dim;
  206. for (var i = 0; i < linksOption.length; i++) {
  207. var linkOption = linksOption[i] || {};
  208. if (checkPropInLink(linkOption[dim + 'AxisId'], axisModel.id) || checkPropInLink(linkOption[dim + 'AxisIndex'], axisModel.componentIndex) || checkPropInLink(linkOption[dim + 'AxisName'], axisModel.name)) {
  209. return i;
  210. }
  211. }
  212. }
  213. function checkPropInLink(linkPropValue, axisPropValue) {
  214. return linkPropValue === 'all' || zrUtil.isArray(linkPropValue) && zrUtil.indexOf(linkPropValue, axisPropValue) >= 0 || linkPropValue === axisPropValue;
  215. }
  216. function fixValue(axisModel) {
  217. var axisInfo = getAxisInfo(axisModel);
  218. if (!axisInfo) {
  219. return;
  220. }
  221. var axisPointerModel = axisInfo.axisPointerModel;
  222. var scale = axisInfo.axis.scale;
  223. var option = axisPointerModel.option;
  224. var status = axisPointerModel.get('status');
  225. var value = axisPointerModel.get('value'); // Parse init value for category and time axis.
  226. if (value != null) {
  227. value = scale.parse(value);
  228. }
  229. var useHandle = isHandleTrigger(axisPointerModel); // If `handle` used, `axisPointer` will always be displayed, so value
  230. // and status should be initialized.
  231. if (status == null) {
  232. option.status = useHandle ? 'show' : 'hide';
  233. }
  234. var extent = scale.getExtent().slice();
  235. extent[0] > extent[1] && extent.reverse();
  236. if ( // Pick a value on axis when initializing.
  237. value == null // If both `handle` and `dataZoom` are used, value may be out of axis extent,
  238. // where we should re-pick a value to keep `handle` displaying normally.
  239. || value > extent[1]) {
  240. // Make handle displayed on the end of the axis when init, which looks better.
  241. value = extent[1];
  242. }
  243. if (value < extent[0]) {
  244. value = extent[0];
  245. }
  246. option.value = value;
  247. if (useHandle) {
  248. option.status = axisInfo.axis.scale.isBlank() ? 'hide' : 'show';
  249. }
  250. }
  251. function getAxisInfo(axisModel) {
  252. var coordSysAxesInfo = (axisModel.ecModel.getComponent('axisPointer') || {}).coordSysAxesInfo;
  253. return coordSysAxesInfo && coordSysAxesInfo.axesInfo[makeKey(axisModel)];
  254. }
  255. function getAxisPointerModel(axisModel) {
  256. var axisInfo = getAxisInfo(axisModel);
  257. return axisInfo && axisInfo.axisPointerModel;
  258. }
  259. function isHandleTrigger(axisPointerModel) {
  260. return !!axisPointerModel.get('handle.show');
  261. }
  262. /**
  263. * @param {module:echarts/model/Model} model
  264. * @return {string} unique key
  265. */
  266. function makeKey(model) {
  267. return model.type + '||' + model.id;
  268. }
  269. exports.collect = collect;
  270. exports.fixValue = fixValue;
  271. exports.getAxisInfo = getAxisInfo;
  272. exports.getAxisPointerModel = getAxisPointerModel;
  273. exports.makeKey = makeKey;