modelHelper.js 10 KB

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