UNPKG

18.6 kBJavaScriptView Raw
1import { __assign, __read, __spreadArray, __values } from "tslib";
2import { contains, filter, find, isArray, isEmpty, isFunction, isNil, isNumberEqual, isObject, memoize, get, values, } from '@antv/util';
3import { FIELD_ORIGIN, GROUP_ATTRS } from '../constant';
4import { getName, inferScaleType } from './scale';
5function snapEqual(v1, v2, scale) {
6 var value1 = scale.translate(v1);
7 var value2 = scale.translate(v2);
8 return isNumberEqual(value1, value2);
9}
10function getXValueByPoint(point, geometry) {
11 var coordinate = geometry.coordinate;
12 var xScale = geometry.getXScale();
13 var range = xScale.range;
14 var rangeMax = range[range.length - 1];
15 var rangeMin = range[0];
16 var invertPoint = coordinate.invert(point);
17 var xValue = invertPoint.x;
18 if (coordinate.isPolar && xValue > (1 + rangeMax) / 2) {
19 xValue = rangeMin; // 极坐标下,scale 的 range 被做过特殊处理
20 }
21 return xScale.translate(xScale.invert(xValue));
22}
23function filterYValue(data, point, geometry) {
24 var coordinate = geometry.coordinate;
25 var yScale = geometry.getYScale();
26 var yField = yScale.field;
27 var invertPoint = coordinate.invert(point);
28 var yValue = yScale.invert(invertPoint.y);
29 var result = find(data, function (obj) {
30 var originData = obj[FIELD_ORIGIN];
31 return originData[yField][0] <= yValue && originData[yField][1] >= yValue;
32 });
33 return result || data[data.length - 1];
34}
35var getXDistance = memoize(function (scale) {
36 if (scale.isCategory) {
37 return 1;
38 }
39 var scaleValues = scale.values; // values 是无序的
40 var length = scaleValues.length;
41 var min = scale.translate(scaleValues[0]);
42 var max = min;
43 for (var index = 0; index < length; index++) {
44 var value = scaleValues[index];
45 // 时间类型需要 translate
46 var numericValue = scale.translate(value);
47 if (numericValue < min) {
48 min = numericValue;
49 }
50 if (numericValue > max) {
51 max = numericValue;
52 }
53 }
54 return (max - min) / (length - 1);
55});
56/**
57 * 获得 tooltip 的 title
58 * @param originData
59 * @param geometry
60 * @param title
61 */
62function getTooltipTitle(originData, geometry, title) {
63 var positionAttr = geometry.getAttribute('position');
64 var fields = positionAttr.getFields();
65 var scales = geometry.scales;
66 var titleField = isFunction(title) || !title ? fields[0] : title;
67 var titleScale = scales[titleField];
68 // 如果创建了该字段对应的 scale,则通过 scale.getText() 方式取值,因为用户可能对数据进行了格式化
69 // 如果没有对应的 scale,则从原始数据中取值,如果原始数据中仍不存在,则直接放回 title 值
70 var tooltipTitle = titleScale ? titleScale.getText(originData[titleField]) : originData[titleField] || titleField;
71 return isFunction(title) ? title(tooltipTitle, originData) : tooltipTitle;
72}
73function getAttributesForLegend(geometry) {
74 var attributes = values(geometry.attributes);
75 return filter(attributes, function (attribute) { return contains(GROUP_ATTRS, attribute.type); });
76}
77function getTooltipValueScale(geometry) {
78 var e_1, _a;
79 var attributes = getAttributesForLegend(geometry);
80 var scale;
81 try {
82 for (var attributes_1 = __values(attributes), attributes_1_1 = attributes_1.next(); !attributes_1_1.done; attributes_1_1 = attributes_1.next()) {
83 var attribute = attributes_1_1.value;
84 var tmpScale = attribute.getScale(attribute.type);
85 if (tmpScale && tmpScale.isLinear) {
86 var tmpScaleDef = get(geometry.scaleDefs, tmpScale.field);
87 var inferedScaleType = inferScaleType(tmpScale, tmpScaleDef, attribute.type, geometry.type);
88 if (inferedScaleType !== 'cat') {
89 // 如果指定字段是非 position 的,同时是连续的
90 scale = tmpScale;
91 break;
92 }
93 }
94 }
95 }
96 catch (e_1_1) { e_1 = { error: e_1_1 }; }
97 finally {
98 try {
99 if (attributes_1_1 && !attributes_1_1.done && (_a = attributes_1.return)) _a.call(attributes_1);
100 }
101 finally { if (e_1) throw e_1.error; }
102 }
103 var xScale = geometry.getXScale();
104 var yScale = geometry.getYScale();
105 return scale || yScale || xScale;
106}
107function getTooltipValue(originData, valueScale) {
108 var field = valueScale.field;
109 var value = originData[field];
110 if (isArray(value)) {
111 var texts = value.map(function (eachValue) {
112 return valueScale.getText(eachValue);
113 });
114 return texts.join('-');
115 }
116 return valueScale.getText(value);
117}
118// 根据原始数据获取 tooltip item 中 name 值
119function getTooltipName(originData, geometry) {
120 var nameScale;
121 var groupScales = geometry.getGroupScales();
122 if (groupScales.length) {
123 // 如果存在分组类型,取第一个分组类型
124 nameScale = groupScales[0];
125 }
126 if (nameScale) {
127 var field = nameScale.field;
128 return nameScale.getText(originData[field]);
129 }
130 var valueScale = getTooltipValueScale(geometry);
131 return getName(valueScale);
132}
133/**
134 * @ignore
135 * Finds data from geometry by point
136 * @param point canvas point
137 * @param data an item of geometry.dataArray
138 * @param geometry
139 * @returns
140 */
141export function findDataByPoint(point, data, geometry) {
142 if (data.length === 0) {
143 return null;
144 }
145 var geometryType = geometry.type;
146 var xScale = geometry.getXScale();
147 var yScale = geometry.getYScale();
148 var xField = xScale.field;
149 var yField = yScale.field;
150 var rst = null;
151 // 热力图采用最小逼近策略查找 point 击中的数据
152 if (geometryType === 'heatmap' || geometryType === 'point') {
153 // 将 point 画布坐标转换为原始数据值
154 var coordinate = geometry.coordinate;
155 var invertPoint = coordinate.invert(point); // 转换成归一化的数据
156 var x = xScale.invert(invertPoint.x); // 转换为原始值
157 var y = yScale.invert(invertPoint.y); // 转换为原始值
158 var min = Infinity;
159 for (var index = 0; index < data.length; index++) {
160 var obj = data[index];
161 var originData = obj[FIELD_ORIGIN];
162 var range = Math.pow((originData[xField] - x), 2) + Math.pow((originData[yField] - y), 2);
163 if (range < min) {
164 min = range;
165 rst = obj;
166 }
167 }
168 return rst;
169 }
170 // 其他 Geometry 类型按照 x 字段数据进行查找
171 var first = data[0];
172 var last = data[data.length - 1];
173 var xValue = getXValueByPoint(point, geometry);
174 var firstXValue = first[FIELD_ORIGIN][xField];
175 var firstYValue = first[FIELD_ORIGIN][yField];
176 var lastXValue = last[FIELD_ORIGIN][xField];
177 var isYArray = yScale.isLinear && isArray(firstYValue); // 考虑 x 维度相同,y 是数组区间的情况
178 // 如果 x 的值是数组
179 if (isArray(firstXValue)) {
180 for (var index = 0; index < data.length; index++) {
181 var record = data[index];
182 var originData = record[FIELD_ORIGIN];
183 // xValue 在 originData[xField] 的数值区间内
184 if (xScale.translate(originData[xField][0]) <= xValue && xScale.translate(originData[xField][1]) >= xValue) {
185 if (isYArray) {
186 // 层叠直方图场景,x 和 y 都是数组区间
187 if (!isArray(rst)) {
188 rst = [];
189 }
190 rst.push(record);
191 }
192 else {
193 rst = record;
194 break;
195 }
196 }
197 }
198 if (isArray(rst)) {
199 rst = filterYValue(rst, point, geometry);
200 }
201 }
202 else {
203 var next = void 0;
204 if (!xScale.isLinear && xScale.type !== 'timeCat') {
205 // x 轴对应的数据为非线性以及非时间类型的数据采用遍历查找
206 for (var index = 0; index < data.length; index++) {
207 var record = data[index];
208 var originData = record[FIELD_ORIGIN];
209 if (snapEqual(originData[xField], xValue, xScale)) {
210 if (isYArray) {
211 if (!isArray(rst)) {
212 rst = [];
213 }
214 rst.push(record);
215 }
216 else {
217 rst = record;
218 break;
219 }
220 }
221 else if (xScale.translate(originData[xField]) <= xValue) {
222 last = record;
223 next = data[index + 1];
224 }
225 }
226 if (isArray(rst)) {
227 rst = filterYValue(rst, point, geometry);
228 }
229 }
230 else {
231 // x 轴对应的数据为线性以及时间类型,进行二分查找,性能更好
232 if ((xValue > xScale.translate(lastXValue) || xValue < xScale.translate(firstXValue)) &&
233 (xValue > xScale.max || xValue < xScale.min)) {
234 // 不在数据范围内
235 return null;
236 }
237 var firstIdx = 0;
238 var lastIdx = data.length - 1;
239 var middleIdx = void 0;
240 while (firstIdx <= lastIdx) {
241 middleIdx = Math.floor((firstIdx + lastIdx) / 2);
242 var item = data[middleIdx][FIELD_ORIGIN][xField];
243 if (snapEqual(item, xValue, xScale)) {
244 return data[middleIdx];
245 }
246 if (xScale.translate(item) <= xScale.translate(xValue)) {
247 firstIdx = middleIdx + 1;
248 last = data[middleIdx];
249 next = data[middleIdx + 1];
250 }
251 else {
252 if (lastIdx === 0) {
253 last = data[0];
254 }
255 lastIdx = middleIdx - 1;
256 }
257 }
258 }
259 if (last && next) {
260 // 计算最逼近的
261 if (Math.abs(xScale.translate(last[FIELD_ORIGIN][xField]) - xValue) >
262 Math.abs(xScale.translate(next[FIELD_ORIGIN][xField]) - xValue)) {
263 last = next;
264 }
265 }
266 }
267 var distance = getXDistance(geometry.getXScale()); // 每个分类间的平均间距
268 if (!rst && Math.abs(xScale.translate(last[FIELD_ORIGIN][xField]) - xValue) <= distance / 2) {
269 rst = last;
270 }
271 return rst;
272}
273/**
274 * @ignore
275 * Gets tooltip items
276 * @param data
277 * @param geometry
278 * @param [title]
279 * @returns
280 */
281export function getTooltipItems(data, geometry, title, showNil) {
282 var e_2, _a;
283 if (title === void 0) { title = ''; }
284 if (showNil === void 0) { showNil = false; }
285 var originData = data[FIELD_ORIGIN];
286 var tooltipTitle = getTooltipTitle(originData, geometry, title);
287 var tooltipOption = geometry.tooltipOption;
288 var defaultColor = geometry.theme.defaultColor;
289 var items = [];
290 var name;
291 var value;
292 function addItem(itemName, itemValue) {
293 if (showNil || (!isNil(itemValue) && itemValue !== '')) {
294 // 值为 null的时候,忽视
295 var item = {
296 title: tooltipTitle,
297 data: originData,
298 mappingData: data,
299 name: itemName,
300 value: itemValue,
301 color: data.color || defaultColor,
302 marker: true,
303 };
304 items.push(item);
305 }
306 }
307 if (isObject(tooltipOption)) {
308 var fields = tooltipOption.fields, callback = tooltipOption.callback;
309 if (callback) {
310 // 用户定义了回调函数
311 var callbackParams = fields.map(function (field) {
312 return data[FIELD_ORIGIN][field];
313 });
314 var cfg = callback.apply(void 0, __spreadArray([], __read(callbackParams), false));
315 var itemCfg = __assign({ data: data[FIELD_ORIGIN], mappingData: data, title: tooltipTitle, color: data.color || defaultColor, marker: true }, cfg);
316 items.push(itemCfg);
317 }
318 else {
319 var scales = geometry.scales;
320 try {
321 for (var fields_1 = __values(fields), fields_1_1 = fields_1.next(); !fields_1_1.done; fields_1_1 = fields_1.next()) {
322 var field = fields_1_1.value;
323 if (!isNil(originData[field])) {
324 // 字段数据为null, undefined 时不显示
325 var scale = scales[field];
326 name = getName(scale);
327 value = scale.getText(originData[field]);
328 addItem(name, value);
329 }
330 }
331 }
332 catch (e_2_1) { e_2 = { error: e_2_1 }; }
333 finally {
334 try {
335 if (fields_1_1 && !fields_1_1.done && (_a = fields_1.return)) _a.call(fields_1);
336 }
337 finally { if (e_2) throw e_2.error; }
338 }
339 }
340 }
341 else {
342 var valueScale = getTooltipValueScale(geometry);
343 // 字段数据为null ,undefined时不显示
344 value = getTooltipValue(originData, valueScale);
345 name = getTooltipName(originData, geometry);
346 addItem(name, value);
347 }
348 return items;
349}
350function getTooltipItemsByFindData(geometry, point, title, tooltipCfg) {
351 var e_3, _a;
352 var showNil = tooltipCfg.showNil;
353 var result = [];
354 var dataArray = geometry.dataArray;
355 if (!isEmpty(dataArray)) {
356 geometry.sort(dataArray); // 先进行排序,便于 tooltip 查找
357 try {
358 for (var dataArray_1 = __values(dataArray), dataArray_1_1 = dataArray_1.next(); !dataArray_1_1.done; dataArray_1_1 = dataArray_1.next()) {
359 var data = dataArray_1_1.value;
360 var record = findDataByPoint(point, data, geometry);
361 if (record) {
362 var elementId = geometry.getElementId(record);
363 var element = geometry.elementsMap[elementId];
364 if (geometry.type === 'heatmap' || element.visible) {
365 // Heatmap 没有 Element
366 // 如果图形元素隐藏了,怎不再 tooltip 上展示相关数据
367 var items = getTooltipItems(record, geometry, title, showNil);
368 if (items.length) {
369 result.push(items);
370 }
371 }
372 }
373 }
374 }
375 catch (e_3_1) { e_3 = { error: e_3_1 }; }
376 finally {
377 try {
378 if (dataArray_1_1 && !dataArray_1_1.done && (_a = dataArray_1.return)) _a.call(dataArray_1);
379 }
380 finally { if (e_3) throw e_3.error; }
381 }
382 }
383 return result;
384}
385function getTooltipItemsByHitShape(geometry, point, title, tooltipCfg) {
386 var showNil = tooltipCfg.showNil;
387 var result = [];
388 var container = geometry.container;
389 var shape = container.getShape(point.x, point.y);
390 if (shape && shape.get('visible') && shape.get('origin')) {
391 var mappingData = shape.get('origin').mappingData;
392 var items = getTooltipItems(mappingData, geometry, title, showNil);
393 if (items.length) {
394 result.push(items);
395 }
396 }
397 return result;
398}
399/**
400 * 不进行递归查找
401 */
402export function findItemsFromView(view, point, tooltipCfg) {
403 var e_4, _a;
404 var result = [];
405 // 先从 view 本身查找
406 var geometries = view.geometries;
407 var shared = tooltipCfg.shared, title = tooltipCfg.title, reversed = tooltipCfg.reversed;
408 try {
409 for (var geometries_1 = __values(geometries), geometries_1_1 = geometries_1.next(); !geometries_1_1.done; geometries_1_1 = geometries_1.next()) {
410 var geometry = geometries_1_1.value;
411 if (geometry.visible && geometry.tooltipOption !== false) {
412 // geometry 可见同时未关闭 tooltip
413 var geometryType = geometry.type;
414 var tooltipItems = void 0;
415 if (['point', 'edge', 'polygon'].includes(geometryType)) {
416 // 始终通过图形拾取
417 tooltipItems = getTooltipItemsByHitShape(geometry, point, title, tooltipCfg);
418 }
419 else if (['area', 'line', 'path', 'heatmap'].includes(geometryType)) {
420 // 如果是 'area', 'line', 'path',始终通过数据查找方法查找 tooltip
421 tooltipItems = getTooltipItemsByFindData(geometry, point, title, tooltipCfg);
422 }
423 else {
424 if (shared !== false) {
425 tooltipItems = getTooltipItemsByFindData(geometry, point, title, tooltipCfg);
426 }
427 else {
428 tooltipItems = getTooltipItemsByHitShape(geometry, point, title, tooltipCfg);
429 }
430 }
431 if (tooltipItems.length) {
432 if (reversed) {
433 tooltipItems.reverse();
434 }
435 // geometry 有可能会有多个 item,因为用户可以设置 geometry.tooltip('x*y*z')
436 result.push(tooltipItems);
437 }
438 }
439 }
440 }
441 catch (e_4_1) { e_4 = { error: e_4_1 }; }
442 finally {
443 try {
444 if (geometries_1_1 && !geometries_1_1.done && (_a = geometries_1.return)) _a.call(geometries_1);
445 }
446 finally { if (e_4) throw e_4.error; }
447 }
448 return result;
449}
450export function findItemsFromViewRecurisive(view, point, tooltipCfg) {
451 var e_5, _a;
452 var result = findItemsFromView(view, point, tooltipCfg);
453 try {
454 // 递归查找,并合并结果
455 for (var _b = __values(view.views), _c = _b.next(); !_c.done; _c = _b.next()) {
456 var childView = _c.value;
457 result = result.concat(findItemsFromView(childView, point, tooltipCfg));
458 }
459 }
460 catch (e_5_1) { e_5 = { error: e_5_1 }; }
461 finally {
462 try {
463 if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
464 }
465 finally { if (e_5) throw e_5.error; }
466 }
467 return result;
468}
469//# sourceMappingURL=tooltip.js.map
\No newline at end of file