mirror of
https://github.com/chartjs/Chart.js.git
synced 2026-01-25 16:42:06 +00:00
317 lines
9.6 KiB
JavaScript
317 lines
9.6 KiB
JavaScript
'use strict';
|
|
|
|
module.exports = function(Chart) {
|
|
var helpers = Chart.helpers;
|
|
|
|
/**
|
|
* Helper function to get relative position for an event
|
|
* @param {Event|IEvent} event - The event to get the position for
|
|
* @param {Chart} chart - The chart
|
|
* @returns {Point} the event position
|
|
*/
|
|
function getRelativePosition(e, chart) {
|
|
if (e.native) {
|
|
return {
|
|
x: e.x,
|
|
y: e.y
|
|
};
|
|
}
|
|
|
|
return helpers.getRelativePosition(e, chart);
|
|
}
|
|
|
|
/**
|
|
* Helper function to traverse all of the visible elements in the chart
|
|
* @param chart {chart} the chart
|
|
* @param handler {Function} the callback to execute for each visible item
|
|
*/
|
|
function parseVisibleItems(chart, handler) {
|
|
var datasets = chart.data.datasets;
|
|
var meta, i, j, ilen, jlen;
|
|
|
|
for (i = 0, ilen = datasets.length; i < ilen; ++i) {
|
|
if (!chart.isDatasetVisible(i)) {
|
|
continue;
|
|
}
|
|
|
|
meta = chart.getDatasetMeta(i);
|
|
for (j = 0, jlen = meta.data.length; j < jlen; ++j) {
|
|
var element = meta.data[j];
|
|
if (!element._view.skip) {
|
|
handler(element);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper function to get the items that intersect the event position
|
|
* @param items {ChartElement[]} elements to filter
|
|
* @param position {Point} the point to be nearest to
|
|
* @return {ChartElement[]} the nearest items
|
|
*/
|
|
function getIntersectItems(chart, position) {
|
|
var elements = [];
|
|
|
|
parseVisibleItems(chart, function(element) {
|
|
if (element.inRange(position.x, position.y)) {
|
|
elements.push(element);
|
|
}
|
|
});
|
|
|
|
return elements;
|
|
}
|
|
|
|
/**
|
|
* Helper function to get the items nearest to the event position considering all visible items in teh chart
|
|
* @param chart {Chart} the chart to look at elements from
|
|
* @param position {Point} the point to be nearest to
|
|
* @param intersect {Boolean} if true, only consider items that intersect the position
|
|
* @param distanceMetric {Function} Optional function to provide the distance between
|
|
* @return {ChartElement[]} the nearest items
|
|
*/
|
|
function getNearestItems(chart, position, intersect, distanceMetric) {
|
|
var minDistance = Number.POSITIVE_INFINITY;
|
|
var nearestItems = [];
|
|
|
|
if (!distanceMetric) {
|
|
distanceMetric = helpers.distanceBetweenPoints;
|
|
}
|
|
|
|
parseVisibleItems(chart, function(element) {
|
|
if (intersect && !element.inRange(position.x, position.y)) {
|
|
return;
|
|
}
|
|
|
|
var center = element.getCenterPoint();
|
|
var distance = distanceMetric(position, center);
|
|
|
|
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;
|
|
}
|
|
|
|
function indexMode(chart, e, options) {
|
|
var position = getRelativePosition(e, chart);
|
|
var distanceMetric = function(pt1, pt2) {
|
|
return Math.abs(pt1.x - pt2.x);
|
|
};
|
|
var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);
|
|
var elements = [];
|
|
|
|
if (!items.length) {
|
|
return [];
|
|
}
|
|
|
|
chart.data.datasets.forEach(function(dataset, datasetIndex) {
|
|
if (chart.isDatasetVisible(datasetIndex)) {
|
|
var meta = chart.getDatasetMeta(datasetIndex),
|
|
element = meta.data[items[0]._index];
|
|
|
|
// don't count items that are skipped (null data)
|
|
if (element && !element._view.skip) {
|
|
elements.push(element);
|
|
}
|
|
}
|
|
});
|
|
|
|
return elements;
|
|
}
|
|
|
|
/**
|
|
* @interface IInteractionOptions
|
|
*/
|
|
/**
|
|
* If true, only consider items that intersect the point
|
|
* @name IInterfaceOptions#boolean
|
|
* @type Boolean
|
|
*/
|
|
|
|
/**
|
|
* Contains interaction related functions
|
|
* @namespace Chart.Interaction
|
|
*/
|
|
Chart.Interaction = {
|
|
// Helper function for different modes
|
|
modes: {
|
|
single: function(chart, e) {
|
|
var position = getRelativePosition(e, chart);
|
|
var elements = [];
|
|
|
|
parseVisibleItems(chart, function(element) {
|
|
if (element.inRange(position.x, position.y)) {
|
|
elements.push(element);
|
|
return elements;
|
|
}
|
|
});
|
|
|
|
return elements.slice(0, 1);
|
|
},
|
|
|
|
/**
|
|
* @function Chart.Interaction.modes.label
|
|
* @deprecated since version 2.4.0
|
|
* @todo remove at version 3
|
|
* @private
|
|
*/
|
|
label: indexMode,
|
|
|
|
/**
|
|
* Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something
|
|
* If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item
|
|
* @function Chart.Interaction.modes.index
|
|
* @since v2.4.0
|
|
* @param chart {chart} the chart we are returning items from
|
|
* @param e {Event} the event we are find things at
|
|
* @param options {IInteractionOptions} options to use during interaction
|
|
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
|
*/
|
|
index: indexMode,
|
|
|
|
/**
|
|
* Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something
|
|
* If the options.intersect is false, we find the nearest item and return the items in that dataset
|
|
* @function Chart.Interaction.modes.dataset
|
|
* @param chart {chart} the chart we are returning items from
|
|
* @param e {Event} the event we are find things at
|
|
* @param options {IInteractionOptions} options to use during interaction
|
|
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
|
*/
|
|
dataset: function(chart, e, options) {
|
|
var position = getRelativePosition(e, chart);
|
|
var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false);
|
|
|
|
if (items.length > 0) {
|
|
items = chart.getDatasetMeta(items[0]._datasetIndex).data;
|
|
}
|
|
|
|
return items;
|
|
},
|
|
|
|
/**
|
|
* @function Chart.Interaction.modes.x-axis
|
|
* @deprecated since version 2.4.0. Use index mode and intersect == true
|
|
* @todo remove at version 3
|
|
* @private
|
|
*/
|
|
'x-axis': function(chart, e) {
|
|
return indexMode(chart, e, true);
|
|
},
|
|
|
|
/**
|
|
* Point mode returns all elements that hit test based on the event position
|
|
* of the event
|
|
* @function Chart.Interaction.modes.intersect
|
|
* @param chart {chart} the chart we are returning items from
|
|
* @param e {Event} the event we are find things at
|
|
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
|
*/
|
|
point: function(chart, e) {
|
|
var position = getRelativePosition(e, chart);
|
|
return getIntersectItems(chart, position);
|
|
},
|
|
|
|
/**
|
|
* nearest mode returns the element closest to the point
|
|
* @function Chart.Interaction.modes.intersect
|
|
* @param chart {chart} the chart we are returning items from
|
|
* @param e {Event} the event we are find things at
|
|
* @param options {IInteractionOptions} options to use
|
|
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
|
*/
|
|
nearest: function(chart, e, options) {
|
|
var position = getRelativePosition(e, chart);
|
|
var nearestItems = getNearestItems(chart, position, options.intersect);
|
|
|
|
// We have multiple items at the same distance from the event. Now sort by smallest
|
|
if (nearestItems.length > 1) {
|
|
nearestItems.sort(function(a, b) {
|
|
var sizeA = a.getArea();
|
|
var sizeB = b.getArea();
|
|
var ret = sizeA - sizeB;
|
|
|
|
if (ret === 0) {
|
|
// if equal sort by dataset index
|
|
ret = a._datasetIndex - b._datasetIndex;
|
|
}
|
|
|
|
return ret;
|
|
});
|
|
}
|
|
|
|
// Return only 1 item
|
|
return nearestItems.slice(0, 1);
|
|
},
|
|
|
|
/**
|
|
* x mode returns the elements that hit-test at the current x coordinate
|
|
* @function Chart.Interaction.modes.x
|
|
* @param chart {chart} the chart we are returning items from
|
|
* @param e {Event} the event we are find things at
|
|
* @param options {IInteractionOptions} options to use
|
|
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
|
*/
|
|
x: function(chart, e, options) {
|
|
var position = getRelativePosition(e, chart);
|
|
var items = [];
|
|
var intersectsItem = false;
|
|
|
|
parseVisibleItems(chart, function(element) {
|
|
if (element.inXRange(position.x)) {
|
|
items.push(element);
|
|
}
|
|
|
|
if (element.inRange(position.x, position.y)) {
|
|
intersectsItem = true;
|
|
}
|
|
});
|
|
|
|
// If we want to trigger on an intersect and we don't have any items
|
|
// that intersect the position, return nothing
|
|
if (options.intersect && !intersectsItem) {
|
|
items = [];
|
|
}
|
|
return items;
|
|
},
|
|
|
|
/**
|
|
* y mode returns the elements that hit-test at the current y coordinate
|
|
* @function Chart.Interaction.modes.y
|
|
* @param chart {chart} the chart we are returning items from
|
|
* @param e {Event} the event we are find things at
|
|
* @param options {IInteractionOptions} options to use
|
|
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
|
*/
|
|
y: function(chart, e, options) {
|
|
var position = getRelativePosition(e, chart);
|
|
var items = [];
|
|
var intersectsItem = false;
|
|
|
|
parseVisibleItems(chart, function(element) {
|
|
if (element.inYRange(position.y)) {
|
|
items.push(element);
|
|
}
|
|
|
|
if (element.inRange(position.x, position.y)) {
|
|
intersectsItem = true;
|
|
}
|
|
});
|
|
|
|
// If we want to trigger on an intersect and we don't have any items
|
|
// that intersect the position, return nothing
|
|
if (options.intersect && !intersectsItem) {
|
|
items = [];
|
|
}
|
|
return items;
|
|
}
|
|
}
|
|
};
|
|
};
|