axisTrigger.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  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. /**
  20. * AUTO-GENERATED FILE. DO NOT MODIFY.
  21. */
  22. /*
  23. * Licensed to the Apache Software Foundation (ASF) under one
  24. * or more contributor license agreements. See the NOTICE file
  25. * distributed with this work for additional information
  26. * regarding copyright ownership. The ASF licenses this file
  27. * to you under the Apache License, Version 2.0 (the
  28. * "License"); you may not use this file except in compliance
  29. * with the License. You may obtain a copy of the License at
  30. *
  31. * http://www.apache.org/licenses/LICENSE-2.0
  32. *
  33. * Unless required by applicable law or agreed to in writing,
  34. * software distributed under the License is distributed on an
  35. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36. * KIND, either express or implied. See the License for the
  37. * specific language governing permissions and limitations
  38. * under the License.
  39. */
  40. import { makeInner } from '../../util/model.js';
  41. import * as modelHelper from './modelHelper.js';
  42. import findPointFromSeries from './findPointFromSeries.js';
  43. import { each, curry, bind, extend } from 'zrender/lib/core/util.js';
  44. var inner = makeInner();
  45. /**
  46. * Basic logic: check all axis, if they do not demand show/highlight,
  47. * then hide/downplay them.
  48. *
  49. * @return content of event obj for echarts.connect.
  50. */
  51. export default function axisTrigger(payload, ecModel, api) {
  52. var currTrigger = payload.currTrigger;
  53. var point = [payload.x, payload.y];
  54. var finder = payload;
  55. var dispatchAction = payload.dispatchAction || bind(api.dispatchAction, api);
  56. var coordSysAxesInfo = ecModel.getComponent('axisPointer').coordSysAxesInfo; // Pending
  57. // See #6121. But we are not able to reproduce it yet.
  58. if (!coordSysAxesInfo) {
  59. return;
  60. }
  61. if (illegalPoint(point)) {
  62. // Used in the default behavior of `connection`: use the sample seriesIndex
  63. // and dataIndex. And also used in the tooltipView trigger.
  64. point = findPointFromSeries({
  65. seriesIndex: finder.seriesIndex,
  66. // Do not use dataIndexInside from other ec instance.
  67. // FIXME: auto detect it?
  68. dataIndex: finder.dataIndex
  69. }, ecModel).point;
  70. }
  71. var isIllegalPoint = illegalPoint(point); // Axis and value can be specified when calling dispatchAction({type: 'updateAxisPointer'}).
  72. // Notice: In this case, it is difficult to get the `point` (which is necessary to show
  73. // tooltip, so if point is not given, we just use the point found by sample seriesIndex
  74. // and dataIndex.
  75. var inputAxesInfo = finder.axesInfo;
  76. var axesInfo = coordSysAxesInfo.axesInfo;
  77. var shouldHide = currTrigger === 'leave' || illegalPoint(point);
  78. var outputPayload = {};
  79. var showValueMap = {};
  80. var dataByCoordSys = {
  81. list: [],
  82. map: {}
  83. };
  84. var updaters = {
  85. showPointer: curry(showPointer, showValueMap),
  86. showTooltip: curry(showTooltip, dataByCoordSys)
  87. }; // Process for triggered axes.
  88. each(coordSysAxesInfo.coordSysMap, function (coordSys, coordSysKey) {
  89. // If a point given, it must be contained by the coordinate system.
  90. var coordSysContainsPoint = isIllegalPoint || coordSys.containPoint(point);
  91. each(coordSysAxesInfo.coordSysAxesInfo[coordSysKey], function (axisInfo, key) {
  92. var axis = axisInfo.axis;
  93. var inputAxisInfo = findInputAxisInfo(inputAxesInfo, axisInfo); // If no inputAxesInfo, no axis is restricted.
  94. if (!shouldHide && coordSysContainsPoint && (!inputAxesInfo || inputAxisInfo)) {
  95. var val = inputAxisInfo && inputAxisInfo.value;
  96. if (val == null && !isIllegalPoint) {
  97. val = axis.pointToData(point);
  98. }
  99. val != null && processOnAxis(axisInfo, val, updaters, false, outputPayload);
  100. }
  101. });
  102. }); // Process for linked axes.
  103. var linkTriggers = {};
  104. each(axesInfo, function (tarAxisInfo, tarKey) {
  105. var linkGroup = tarAxisInfo.linkGroup; // If axis has been triggered in the previous stage, it should not be triggered by link.
  106. if (linkGroup && !showValueMap[tarKey]) {
  107. each(linkGroup.axesInfo, function (srcAxisInfo, srcKey) {
  108. var srcValItem = showValueMap[srcKey]; // If srcValItem exist, source axis is triggered, so link to target axis.
  109. if (srcAxisInfo !== tarAxisInfo && srcValItem) {
  110. var val = srcValItem.value;
  111. linkGroup.mapper && (val = tarAxisInfo.axis.scale.parse(linkGroup.mapper(val, makeMapperParam(srcAxisInfo), makeMapperParam(tarAxisInfo))));
  112. linkTriggers[tarAxisInfo.key] = val;
  113. }
  114. });
  115. }
  116. });
  117. each(linkTriggers, function (val, tarKey) {
  118. processOnAxis(axesInfo[tarKey], val, updaters, true, outputPayload);
  119. });
  120. updateModelActually(showValueMap, axesInfo, outputPayload);
  121. dispatchTooltipActually(dataByCoordSys, point, payload, dispatchAction);
  122. dispatchHighDownActually(axesInfo, dispatchAction, api);
  123. return outputPayload;
  124. }
  125. function processOnAxis(axisInfo, newValue, updaters, noSnap, outputFinder) {
  126. var axis = axisInfo.axis;
  127. if (axis.scale.isBlank() || !axis.containData(newValue)) {
  128. return;
  129. }
  130. if (!axisInfo.involveSeries) {
  131. updaters.showPointer(axisInfo, newValue);
  132. return;
  133. } // Heavy calculation. So put it after axis.containData checking.
  134. var payloadInfo = buildPayloadsBySeries(newValue, axisInfo);
  135. var payloadBatch = payloadInfo.payloadBatch;
  136. var snapToValue = payloadInfo.snapToValue; // Fill content of event obj for echarts.connect.
  137. // By default use the first involved series data as a sample to connect.
  138. if (payloadBatch[0] && outputFinder.seriesIndex == null) {
  139. extend(outputFinder, payloadBatch[0]);
  140. } // If no linkSource input, this process is for collecting link
  141. // target, where snap should not be accepted.
  142. if (!noSnap && axisInfo.snap) {
  143. if (axis.containData(snapToValue) && snapToValue != null) {
  144. newValue = snapToValue;
  145. }
  146. }
  147. updaters.showPointer(axisInfo, newValue, payloadBatch); // Tooltip should always be snapToValue, otherwise there will be
  148. // incorrect "axis value ~ series value" mapping displayed in tooltip.
  149. updaters.showTooltip(axisInfo, payloadInfo, snapToValue);
  150. }
  151. function buildPayloadsBySeries(value, axisInfo) {
  152. var axis = axisInfo.axis;
  153. var dim = axis.dim;
  154. var snapToValue = value;
  155. var payloadBatch = [];
  156. var minDist = Number.MAX_VALUE;
  157. var minDiff = -1;
  158. each(axisInfo.seriesModels, function (series, idx) {
  159. var dataDim = series.getData().mapDimensionsAll(dim);
  160. var seriesNestestValue;
  161. var dataIndices;
  162. if (series.getAxisTooltipData) {
  163. var result = series.getAxisTooltipData(dataDim, value, axis);
  164. dataIndices = result.dataIndices;
  165. seriesNestestValue = result.nestestValue;
  166. } else {
  167. dataIndices = series.getData().indicesOfNearest(dataDim[0], value, // Add a threshold to avoid find the wrong dataIndex
  168. // when data length is not same.
  169. // false,
  170. axis.type === 'category' ? 0.5 : null);
  171. if (!dataIndices.length) {
  172. return;
  173. }
  174. seriesNestestValue = series.getData().get(dataDim[0], dataIndices[0]);
  175. }
  176. if (seriesNestestValue == null || !isFinite(seriesNestestValue)) {
  177. return;
  178. }
  179. var diff = value - seriesNestestValue;
  180. var dist = Math.abs(diff); // Consider category case
  181. if (dist <= minDist) {
  182. if (dist < minDist || diff >= 0 && minDiff < 0) {
  183. minDist = dist;
  184. minDiff = diff;
  185. snapToValue = seriesNestestValue;
  186. payloadBatch.length = 0;
  187. }
  188. each(dataIndices, function (dataIndex) {
  189. payloadBatch.push({
  190. seriesIndex: series.seriesIndex,
  191. dataIndexInside: dataIndex,
  192. dataIndex: series.getData().getRawIndex(dataIndex)
  193. });
  194. });
  195. }
  196. });
  197. return {
  198. payloadBatch: payloadBatch,
  199. snapToValue: snapToValue
  200. };
  201. }
  202. function showPointer(showValueMap, axisInfo, value, payloadBatch) {
  203. showValueMap[axisInfo.key] = {
  204. value: value,
  205. payloadBatch: payloadBatch
  206. };
  207. }
  208. function showTooltip(dataByCoordSys, axisInfo, payloadInfo, value) {
  209. var payloadBatch = payloadInfo.payloadBatch;
  210. var axis = axisInfo.axis;
  211. var axisModel = axis.model;
  212. var axisPointerModel = axisInfo.axisPointerModel; // If no data, do not create anything in dataByCoordSys,
  213. // whose length will be used to judge whether dispatch action.
  214. if (!axisInfo.triggerTooltip || !payloadBatch.length) {
  215. return;
  216. }
  217. var coordSysModel = axisInfo.coordSys.model;
  218. var coordSysKey = modelHelper.makeKey(coordSysModel);
  219. var coordSysItem = dataByCoordSys.map[coordSysKey];
  220. if (!coordSysItem) {
  221. coordSysItem = dataByCoordSys.map[coordSysKey] = {
  222. coordSysId: coordSysModel.id,
  223. coordSysIndex: coordSysModel.componentIndex,
  224. coordSysType: coordSysModel.type,
  225. coordSysMainType: coordSysModel.mainType,
  226. dataByAxis: []
  227. };
  228. dataByCoordSys.list.push(coordSysItem);
  229. }
  230. coordSysItem.dataByAxis.push({
  231. axisDim: axis.dim,
  232. axisIndex: axisModel.componentIndex,
  233. axisType: axisModel.type,
  234. axisId: axisModel.id,
  235. value: value,
  236. // Caustion: viewHelper.getValueLabel is actually on "view stage", which
  237. // depends that all models have been updated. So it should not be performed
  238. // here. Considering axisPointerModel used here is volatile, which is hard
  239. // to be retrieve in TooltipView, we prepare parameters here.
  240. valueLabelOpt: {
  241. precision: axisPointerModel.get(['label', 'precision']),
  242. formatter: axisPointerModel.get(['label', 'formatter'])
  243. },
  244. seriesDataIndices: payloadBatch.slice()
  245. });
  246. }
  247. function updateModelActually(showValueMap, axesInfo, outputPayload) {
  248. var outputAxesInfo = outputPayload.axesInfo = []; // Basic logic: If no 'show' required, 'hide' this axisPointer.
  249. each(axesInfo, function (axisInfo, key) {
  250. var option = axisInfo.axisPointerModel.option;
  251. var valItem = showValueMap[key];
  252. if (valItem) {
  253. !axisInfo.useHandle && (option.status = 'show');
  254. option.value = valItem.value; // For label formatter param and highlight.
  255. option.seriesDataIndices = (valItem.payloadBatch || []).slice();
  256. } // When always show (e.g., handle used), remain
  257. // original value and status.
  258. else {
  259. // If hide, value still need to be set, consider
  260. // click legend to toggle axis blank.
  261. !axisInfo.useHandle && (option.status = 'hide');
  262. } // If status is 'hide', should be no info in payload.
  263. option.status === 'show' && outputAxesInfo.push({
  264. axisDim: axisInfo.axis.dim,
  265. axisIndex: axisInfo.axis.model.componentIndex,
  266. value: option.value
  267. });
  268. });
  269. }
  270. function dispatchTooltipActually(dataByCoordSys, point, payload, dispatchAction) {
  271. // Basic logic: If no showTip required, hideTip will be dispatched.
  272. if (illegalPoint(point) || !dataByCoordSys.list.length) {
  273. dispatchAction({
  274. type: 'hideTip'
  275. });
  276. return;
  277. } // In most case only one axis (or event one series is used). It is
  278. // convenient to fetch payload.seriesIndex and payload.dataIndex
  279. // directly. So put the first seriesIndex and dataIndex of the first
  280. // axis on the payload.
  281. var sampleItem = ((dataByCoordSys.list[0].dataByAxis[0] || {}).seriesDataIndices || [])[0] || {};
  282. dispatchAction({
  283. type: 'showTip',
  284. escapeConnect: true,
  285. x: point[0],
  286. y: point[1],
  287. tooltipOption: payload.tooltipOption,
  288. position: payload.position,
  289. dataIndexInside: sampleItem.dataIndexInside,
  290. dataIndex: sampleItem.dataIndex,
  291. seriesIndex: sampleItem.seriesIndex,
  292. dataByCoordSys: dataByCoordSys.list
  293. });
  294. }
  295. function dispatchHighDownActually(axesInfo, dispatchAction, api) {
  296. // FIXME
  297. // highlight status modification should be a stage of main process?
  298. // (Consider confilct (e.g., legend and axisPointer) and setOption)
  299. var zr = api.getZr();
  300. var highDownKey = 'axisPointerLastHighlights';
  301. var lastHighlights = inner(zr)[highDownKey] || {};
  302. var newHighlights = inner(zr)[highDownKey] = {}; // Update highlight/downplay status according to axisPointer model.
  303. // Build hash map and remove duplicate incidentally.
  304. each(axesInfo, function (axisInfo, key) {
  305. var option = axisInfo.axisPointerModel.option;
  306. option.status === 'show' && axisInfo.triggerEmphasis && each(option.seriesDataIndices, function (batchItem) {
  307. var key = batchItem.seriesIndex + ' | ' + batchItem.dataIndex;
  308. newHighlights[key] = batchItem;
  309. });
  310. }); // Diff.
  311. var toHighlight = [];
  312. var toDownplay = [];
  313. each(lastHighlights, function (batchItem, key) {
  314. !newHighlights[key] && toDownplay.push(batchItem);
  315. });
  316. each(newHighlights, function (batchItem, key) {
  317. !lastHighlights[key] && toHighlight.push(batchItem);
  318. });
  319. toDownplay.length && api.dispatchAction({
  320. type: 'downplay',
  321. escapeConnect: true,
  322. // Not blur others when highlight in axisPointer.
  323. notBlur: true,
  324. batch: toDownplay
  325. });
  326. toHighlight.length && api.dispatchAction({
  327. type: 'highlight',
  328. escapeConnect: true,
  329. // Not blur others when highlight in axisPointer.
  330. notBlur: true,
  331. batch: toHighlight
  332. });
  333. }
  334. function findInputAxisInfo(inputAxesInfo, axisInfo) {
  335. for (var i = 0; i < (inputAxesInfo || []).length; i++) {
  336. var inputAxisInfo = inputAxesInfo[i];
  337. if (axisInfo.axis.dim === inputAxisInfo.axisDim && axisInfo.axis.model.componentIndex === inputAxisInfo.axisIndex) {
  338. return inputAxisInfo;
  339. }
  340. }
  341. }
  342. function makeMapperParam(axisInfo) {
  343. var axisModel = axisInfo.axis.model;
  344. var item = {};
  345. var dim = item.axisDim = axisInfo.axis.dim;
  346. item.axisIndex = item[dim + 'AxisIndex'] = axisModel.componentIndex;
  347. item.axisName = item[dim + 'AxisName'] = axisModel.name;
  348. item.axisId = item[dim + 'AxisId'] = axisModel.id;
  349. return item;
  350. }
  351. function illegalPoint(point) {
  352. return !point || point[0] == null || isNaN(point[0]) || point[1] == null || isNaN(point[1]);
  353. }