325 lines
12 KiB
Java
325 lines
12 KiB
Java
import { __assign } from "tslib";
|
||
import { contains, filter, find, hasKey, isArray, isNil, isNumberEqual, isObject, memoize, values, } from '@antv/util';
|
||
import { FIELD_ORIGIN, GROUP_ATTRS } from '../constant';
|
||
import { getName } from './scale';
|
||
function snapEqual(v1, v2, scale) {
|
||
var value1 = scale.translate(v1);
|
||
var value2 = scale.translate(v2);
|
||
return isNumberEqual(value1, value2);
|
||
}
|
||
function getXValueByPoint(point, geometry) {
|
||
var coordinate = geometry.coordinate;
|
||
var xScale = geometry.getXScale();
|
||
var range = xScale.range;
|
||
var rangeMax = range[range.length - 1];
|
||
var rangeMin = range[0];
|
||
var invertPoint = coordinate.invert(point);
|
||
var xValue = invertPoint.x;
|
||
if (coordinate.isPolar && xValue > (1 + rangeMax) / 2) {
|
||
xValue = rangeMin; // 极坐标下,scale 的 range 被做过特殊处理
|
||
}
|
||
return xScale.translate(xScale.invert(xValue));
|
||
}
|
||
function filterYValue(data, point, geometry) {
|
||
var coordinate = geometry.coordinate;
|
||
var yScale = geometry.getYScale();
|
||
var yField = yScale.field;
|
||
var invertPoint = coordinate.invert(point);
|
||
var yValue = yScale.invert(invertPoint.y);
|
||
var result = find(data, function (obj) {
|
||
var originData = obj[FIELD_ORIGIN];
|
||
return originData[yField][0] <= yValue && originData[yField][1] >= yValue;
|
||
});
|
||
return result || data[data.length - 1];
|
||
}
|
||
var getXDistance = memoize(function (scale) {
|
||
if (scale.isCategory) {
|
||
return 1;
|
||
}
|
||
var scaleValues = scale.values; // values 是无序的
|
||
var length = scaleValues.length;
|
||
var min = scale.translate(scaleValues[0]);
|
||
var max = min;
|
||
for (var index = 0; index < length; index++) {
|
||
var value = scaleValues[index];
|
||
// 时间类型需要 translate
|
||
var numericValue = scale.translate(value);
|
||
if (numericValue < min) {
|
||
min = numericValue;
|
||
}
|
||
if (numericValue > max) {
|
||
max = numericValue;
|
||
}
|
||
}
|
||
return (max - min) / (length - 1);
|
||
});
|
||
function getTooltipTitle(originData, geometry, title) {
|
||
var titleField = title;
|
||
if (!title) {
|
||
var positionAttr = geometry.getAttribute('position');
|
||
var fields = positionAttr.getFields();
|
||
titleField = fields[0];
|
||
}
|
||
var scales = geometry.scales;
|
||
if (scales[titleField]) {
|
||
// 如果创建了该字段对应的 scale,则通过 scale.getText() 方式取值,因为用户可能对数据进行了格式化
|
||
return scales[titleField].getText(originData[titleField]);
|
||
}
|
||
// 如果没有对应的 scale,则从原始数据中取值,如果原始数据中仍不存在,则直接放回 title 值
|
||
return hasKey(originData, titleField) ? originData[titleField] : titleField;
|
||
}
|
||
function getAttributesForLegend(geometry) {
|
||
var attributes = values(geometry.attributes);
|
||
return filter(attributes, function (attribute) { return contains(GROUP_ATTRS, attribute.type); });
|
||
}
|
||
function getTooltipValueScale(geometry) {
|
||
var attributes = getAttributesForLegend(geometry);
|
||
var scale;
|
||
for (var _i = 0, attributes_1 = attributes; _i < attributes_1.length; _i++) {
|
||
var attribute = attributes_1[_i];
|
||
var tmpScale = attribute.getScale(attribute.type);
|
||
if (tmpScale && tmpScale.isLinear) {
|
||
// 如果指定字段是非 position 的,同时是连续的
|
||
scale = tmpScale;
|
||
break;
|
||
}
|
||
}
|
||
var xScale = geometry.getXScale();
|
||
var yScale = geometry.getYScale();
|
||
return scale || yScale || xScale;
|
||
}
|
||
function getTooltipValue(originData, valueScale) {
|
||
var field = valueScale.field;
|
||
var value = originData[field];
|
||
if (isArray(value)) {
|
||
var texts = value.map(function (eachValue) {
|
||
return valueScale.getText(eachValue);
|
||
});
|
||
return texts.join('-');
|
||
}
|
||
return valueScale.getText(value);
|
||
}
|
||
// 根据原始数据获取 tooltip item 中 name 值
|
||
function getTooltipName(originData, geometry) {
|
||
var nameScale;
|
||
var groupScales = geometry.getGroupScales();
|
||
if (groupScales.length) {
|
||
// 如果存在分组类型,取第一个分组类型
|
||
nameScale = groupScales[0];
|
||
}
|
||
if (nameScale) {
|
||
var field = nameScale.field;
|
||
return nameScale.getText(originData[field]);
|
||
}
|
||
var valueScale = getTooltipValueScale(geometry);
|
||
return getName(valueScale);
|
||
}
|
||
/**
|
||
* @ignore
|
||
* Finds data from geometry by point
|
||
* @param point canvas point
|
||
* @param data an item of geometry.dataArray
|
||
* @param geometry
|
||
* @returns
|
||
*/
|
||
export function findDataByPoint(point, data, geometry) {
|
||
if (data.length === 0) {
|
||
return null;
|
||
}
|
||
var geometryType = geometry.type;
|
||
var xScale = geometry.getXScale();
|
||
var yScale = geometry.getYScale();
|
||
var xField = xScale.field;
|
||
var yField = yScale.field;
|
||
var rst = null;
|
||
// 热力图采用最小逼近策略查找 point 击中的数据
|
||
if (geometryType === 'heatmap' || geometryType === 'point') {
|
||
// 将 point 画布坐标转换为原始数据值
|
||
var coordinate = geometry.coordinate;
|
||
var invertPoint = coordinate.invert(point); // 转换成归一化的数据
|
||
var x = xScale.invert(invertPoint.x); // 转换为原始值
|
||
var y = yScale.invert(invertPoint.y); // 转换为原始值
|
||
var min = Infinity;
|
||
for (var index = 0; index < data.length; index++) {
|
||
var obj = data[index];
|
||
var originData = obj[FIELD_ORIGIN];
|
||
var range = Math.pow((originData[xField] - x), 2) + Math.pow((originData[yField] - y), 2);
|
||
if (range < min) {
|
||
min = range;
|
||
rst = obj;
|
||
}
|
||
}
|
||
return rst;
|
||
}
|
||
// 其他 Geometry 类型按照 x 字段数据进行查找
|
||
var first = data[0];
|
||
var last = data[data.length - 1];
|
||
var xValue = getXValueByPoint(point, geometry);
|
||
var firstXValue = first[FIELD_ORIGIN][xField];
|
||
var firstYValue = first[FIELD_ORIGIN][yField];
|
||
var lastXValue = last[FIELD_ORIGIN][xField];
|
||
var isYArray = yScale.isLinear && isArray(firstYValue); // 考虑 x 维度相同,y 是数组区间的情况
|
||
// 如果 x 的值是数组
|
||
if (isArray(firstXValue)) {
|
||
for (var index = 0; index < data.length; index++) {
|
||
var record = data[index];
|
||
var originData = record[FIELD_ORIGIN];
|
||
// xValue 在 originData[xField] 的数值区间内
|
||
if (xScale.translate(originData[xField][0]) <= xValue && xScale.translate(originData[xField][1]) >= xValue) {
|
||
if (isYArray) {
|
||
// 层叠直方图场景,x 和 y 都是数组区间
|
||
if (!isArray(rst)) {
|
||
rst = [];
|
||
}
|
||
rst.push(record);
|
||
}
|
||
else {
|
||
rst = record;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (isArray(rst)) {
|
||
rst = filterYValue(rst, point, geometry);
|
||
}
|
||
}
|
||
else {
|
||
var next = void 0;
|
||
if (!xScale.isLinear && xScale.type !== 'timeCat') {
|
||
// x 轴对应的数据为非线性以及非时间类型的数据采用遍历查找
|
||
for (var index = 0; index < data.length; index++) {
|
||
var record = data[index];
|
||
var originData = record[FIELD_ORIGIN];
|
||
if (snapEqual(originData[xField], xValue, xScale)) {
|
||
if (isYArray) {
|
||
if (!isArray(rst)) {
|
||
rst = [];
|
||
}
|
||
rst.push(record);
|
||
}
|
||
else {
|
||
rst = record;
|
||
break;
|
||
}
|
||
}
|
||
else if (xScale.translate(originData[xField]) <= xValue) {
|
||
last = record;
|
||
next = data[index + 1];
|
||
}
|
||
}
|
||
if (isArray(rst)) {
|
||
rst = filterYValue(rst, point, geometry);
|
||
}
|
||
}
|
||
else {
|
||
// x 轴对应的数据为线性以及时间类型,进行二分查找,性能更好
|
||
if ((xValue > xScale.translate(lastXValue) || xValue < xScale.translate(firstXValue))
|
||
&& (xValue > xScale.max || xValue < xScale.min)) {
|
||
// 不在数据范围内
|
||
return null;
|
||
}
|
||
var firstIdx = 0;
|
||
var lastIdx = data.length - 1;
|
||
var middleIdx = void 0;
|
||
while (firstIdx <= lastIdx) {
|
||
middleIdx = Math.floor((firstIdx + lastIdx) / 2);
|
||
var item = data[middleIdx][FIELD_ORIGIN][xField];
|
||
if (snapEqual(item, xValue, xScale)) {
|
||
return data[middleIdx];
|
||
}
|
||
if (xScale.translate(item) <= xScale.translate(xValue)) {
|
||
firstIdx = middleIdx + 1;
|
||
last = data[middleIdx];
|
||
next = data[middleIdx + 1];
|
||
}
|
||
else {
|
||
if (lastIdx === 0) {
|
||
last = data[0];
|
||
}
|
||
lastIdx = middleIdx - 1;
|
||
}
|
||
}
|
||
}
|
||
if (last && next) {
|
||
// 计算最逼近的
|
||
if (Math.abs(xScale.translate(last[FIELD_ORIGIN][xField]) - xValue) >
|
||
Math.abs(xScale.translate(next[FIELD_ORIGIN][xField]) - xValue)) {
|
||
last = next;
|
||
}
|
||
}
|
||
}
|
||
var distance = getXDistance(geometry.getXScale()); // 每个分类间的平均间距
|
||
if (!rst && Math.abs(xScale.translate(last[FIELD_ORIGIN][xField]) - xValue) <= distance / 2) {
|
||
rst = last;
|
||
}
|
||
return rst;
|
||
}
|
||
/**
|
||
* @ignore
|
||
* Gets tooltip items
|
||
* @param data
|
||
* @param geometry
|
||
* @param [title]
|
||
* @returns
|
||
*/
|
||
export function getTooltipItems(data, geometry, title) {
|
||
if (title === void 0) { title = ''; }
|
||
var originData = data[FIELD_ORIGIN];
|
||
var tooltipTitle = getTooltipTitle(originData, geometry, title);
|
||
var tooltipOption = geometry.tooltipOption;
|
||
var defaultColor = geometry.theme.defaultColor;
|
||
var items = [];
|
||
var name;
|
||
var value;
|
||
function addItem(itemName, itemValue) {
|
||
if (!isNil(itemValue) && itemValue !== '') {
|
||
// 值为 null的时候,忽视
|
||
var item = {
|
||
title: tooltipTitle,
|
||
data: originData,
|
||
mappingData: data,
|
||
name: itemName || tooltipTitle,
|
||
value: itemValue,
|
||
color: data.color || defaultColor,
|
||
marker: true,
|
||
};
|
||
items.push(item);
|
||
}
|
||
}
|
||
if (isObject(tooltipOption)) {
|
||
var fields = tooltipOption.fields, callback = tooltipOption.callback;
|
||
if (callback) {
|
||
// 用户定义了回调函数
|
||
var callbackParams = fields.map(function (field) {
|
||
return data[FIELD_ORIGIN][field];
|
||
});
|
||
var cfg = callback.apply(void 0, callbackParams);
|
||
var itemCfg = __assign({ data: data[FIELD_ORIGIN], mappingData: data, title: tooltipTitle, color: data.color || defaultColor, marker: true }, cfg);
|
||
items.push(itemCfg);
|
||
}
|
||
else {
|
||
var scales = geometry.scales;
|
||
for (var _i = 0, fields_1 = fields; _i < fields_1.length; _i++) {
|
||
var field = fields_1[_i];
|
||
if (!isNil(originData[field])) {
|
||
// 字段数据为null, undefined 时不显示
|
||
var scale = scales[field];
|
||
name = getName(scale);
|
||
value = scale.getText(originData[field]);
|
||
addItem(name, value);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
var valueScale = getTooltipValueScale(geometry);
|
||
if (!isNil(originData[valueScale.field])) {
|
||
// 字段数据为null ,undefined时不显示
|
||
value = getTooltipValue(originData, valueScale);
|
||
name = getTooltipName(originData, geometry);
|
||
addItem(name, value);
|
||
}
|
||
}
|
||
return items;
|
||
}
|
||
//# sourceMappingURL=tooltip.js.map |