-
Notifications
You must be signed in to change notification settings - Fork 319
/
interaction.js
84 lines (75 loc) · 2.83 KB
/
interaction.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import {distanceBetweenPoints} from 'chart.js/helpers';
const interaction = {
modes: {
/**
* Point mode returns all elements that hit test based on the event position
* @param {Object} state - the state of the plugin
* @param {ChartEvent} event - the event we are find things at
* @return {Element[]} - elements that are found
*/
point(state, event) {
return getIntersectItems(state, event);
},
/**
* Nearest mode returns the element closest to the event position
* @param {Object} state - the state of the plugin
* @param {ChartEvent} event - the event we are find things at
* @param {options} options - interaction options to use
* @return {Element[]} - elements that are found (only 1 element)
*/
nearest(state, event, options) {
return getNearestItem(state, event, options);
},
}
};
/**
* Returns all elements that hit test based on the event position
* @param {Object} state - the state of the plugin
* @param {ChartEvent} event - the event we are find things at
* @param {options} options - interaction options to use
* @return {Element[]} - elements that are found
*/
export function getElements(state, event, options) {
const mode = interaction.modes[options.mode] || interaction.modes.nearest;
return mode(state, event, options);
}
function getIntersectItems(state, event) {
return state.visibleElements.filter((element) => element.inRange(event.x, event.y, true));
}
function inRangeByAxis(element, event, axis) {
if (axis === 'x') {
return element.inXRange(event.x, event.y, true);
} else if (axis === 'y') {
return element.inYRange(event.x, event.y, true);
}
return element.inXRange(event.x, event.y, true) || element.inYRange(event.x, event.y, true);
}
function getPointByAxis(event, center, axis) {
if (axis === 'x') {
return {x: event.x, y: center.y};
} else if (axis === 'y') {
return {x: center.x, y: event.y};
}
return center;
}
function getNearestItem(state, event, options) {
const axis = options.axis || 'xy';
let minDistance = Number.POSITIVE_INFINITY;
return state.visibleElements
.filter((element) => options.intersect ? element.inRange(event.x, event.y, true) : inRangeByAxis(element, event, axis))
.reduce((nearestItems, element) => {
const center = element.getCenterPoint();
const evenPoint = getPointByAxis(event, center, axis);
const distance = distanceBetweenPoints(event, evenPoint);
if (distance < minDistance) {
nearestItems = [element];
minDistance = distance;
} else if (distance === minDistance) {
// Can have multiple items at the same distance in which case we sort by size
nearestItems.push(element);
}
return nearestItems;
}, [])
.sort((a, b) => a._index - b._index)
.slice(0, 1); // return only the top item;
}