-
Notifications
You must be signed in to change notification settings - Fork 319
/
interaction.js
101 lines (91 loc) · 3.61 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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
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 filterElements(state, event, {intersect: true});
},
/**
* 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 {Object} options - interaction options to use
* @return {Element[]} - elements that are found (only 1 element)
*/
nearest(state, event, options) {
return getNearestItem(state, event, options);
},
/**
* x mode returns the elements that hit-test at the current x coordinate
* @param {Object} state - the state of the plugin
* @param {ChartEvent} event - the event we are find things at
* @param {Object} options - interaction options to use
* @return {Element[]} - elements that are found
*/
x(state, event, options) {
return filterElements(state, event, {intersect: options.intersect, axis: 'x'});
},
/**
* y mode returns the elements that hit-test at the current y coordinate
* @param {Object} state - the state of the plugin
* @param {ChartEvent} event - the event we are find things at
* @param {Object} options - interaction options to use
* @return {Element[]} - elements that are found
*/
y(state, event, options) {
return filterElements(state, event, {intersect: options.intersect, axis: 'y'});
}
}
};
/**
* 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 {Object} 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 inRangeByAxis(element, event, axis) {
if (axis !== 'x' && axis !== 'y') {
return element.inRange(event.x, event.y, 'x', true) || element.inRange(event.x, event.y, 'y', true);
}
return element.inRange(event.x, event.y, axis, 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 filterElements(state, event, options) {
return state.visibleElements.filter((element) => options.intersect ? element.inRange(event.x, event.y) : inRangeByAxis(element, event, options.axis));
}
function getNearestItem(state, event, options) {
let minDistance = Number.POSITIVE_INFINITY;
return filterElements(state, event, options)
.reduce((nearestItems, element) => {
const center = element.getCenterPoint();
const evenPoint = getPointByAxis(event, center, options.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;
}