mirror of
https://github.com/chartjs/Chart.js.git
synced 2026-03-24 00:56:51 +01:00
Limit interactions to chartArea (+/-0.5px) (#6943)
Limit interactions to chartArea (+/-0.5px)
This commit is contained in:
committed by
Evert Timberg
parent
5054ecfd7e
commit
f1677b6652
@@ -19,6 +19,7 @@ Chart.js 3.0 introduces a number of breaking changes. Chart.js 2.0 was released
|
||||
|
||||
### Interactions
|
||||
|
||||
* `interactions` are now limited to the chart area
|
||||
* `{mode: 'label'}` was replaced with `{mode: 'index'}`
|
||||
* `{mode: 'single'}` was replaced with `{mode: 'nearest', intersect: true}`
|
||||
* `modes['X-axis']` was replaced with `{mode: 'index', intersect: false}`
|
||||
@@ -117,6 +118,7 @@ Animation system was completely rewritten in Chart.js v3. Each property can now
|
||||
* `Element._view`
|
||||
* `TimeScale._getPixelForOffset`
|
||||
* `TimeScale.getLabelWidth`
|
||||
* `Tooltip._lastActive`
|
||||
|
||||
### Renamed
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@ defaults._set('horizontalBar', {
|
||||
scales: {
|
||||
x: {
|
||||
type: 'linear',
|
||||
position: 'bottom'
|
||||
position: 'bottom',
|
||||
beginAtZero: true
|
||||
},
|
||||
y: {
|
||||
type: 'category',
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import helpers from '../helpers/index';
|
||||
import {isNumber} from '../helpers/helpers.math';
|
||||
import {_isPointInArea} from '../helpers/helpers.canvas';
|
||||
|
||||
/**
|
||||
* Helper function to get relative position for an event
|
||||
@@ -43,6 +44,8 @@ function evaluateAllVisibleItems(chart, handler) {
|
||||
/**
|
||||
* Helper function to check the items at the hovered index on the index scale
|
||||
* @param {Chart} chart - the chart
|
||||
* @param {string} axis - the axis mode. x|y|xy
|
||||
* @param {object} position - the point to be nearest to
|
||||
* @param {function} handler - the callback to execute for each visible item
|
||||
* @return whether all scales were of a suitable type
|
||||
*/
|
||||
@@ -91,13 +94,18 @@ function getDistanceMetricForAxis(axis) {
|
||||
|
||||
/**
|
||||
* Helper function to get the items that intersect the event position
|
||||
* @param {ChartElement[]} items - elements to filter
|
||||
* @param {Chart} chart - the chart
|
||||
* @param {object} position - the point to be nearest to
|
||||
* @param {string} axis - the axis mode. x|y|xy
|
||||
* @return {ChartElement[]} the nearest items
|
||||
*/
|
||||
function getIntersectItems(chart, position, axis) {
|
||||
const items = [];
|
||||
|
||||
if (!_isPointInArea(position, chart.chartArea)) {
|
||||
return items;
|
||||
}
|
||||
|
||||
const evaluationFunc = function(element, datasetIndex, index) {
|
||||
if (element.inRange(position.x, position.y)) {
|
||||
items.push({element, datasetIndex, index});
|
||||
@@ -126,6 +134,10 @@ function getNearestItems(chart, position, axis, intersect) {
|
||||
let minDistance = Number.POSITIVE_INFINITY;
|
||||
let items = [];
|
||||
|
||||
if (!_isPointInArea(position, chart.chartArea)) {
|
||||
return items;
|
||||
}
|
||||
|
||||
const evaluationFunc = function(element, datasetIndex, index) {
|
||||
if (intersect && !element.inRange(position.x, position.y)) {
|
||||
return;
|
||||
|
||||
@@ -219,10 +219,10 @@ function createTooltipItem(chart, item) {
|
||||
const {label, value} = chart.getDatasetMeta(datasetIndex).controller._getLabelAndValue(index);
|
||||
|
||||
return {
|
||||
label: label,
|
||||
value: value,
|
||||
index: index,
|
||||
datasetIndex: datasetIndex
|
||||
label,
|
||||
value,
|
||||
index,
|
||||
datasetIndex
|
||||
};
|
||||
}
|
||||
|
||||
@@ -452,7 +452,6 @@ class Tooltip extends Element {
|
||||
const me = this;
|
||||
me.opacity = 0;
|
||||
me._active = [];
|
||||
me._lastActive = [];
|
||||
me.initialize();
|
||||
}
|
||||
|
||||
@@ -962,28 +961,26 @@ class Tooltip extends Element {
|
||||
* @returns {boolean} true if the tooltip changed
|
||||
*/
|
||||
handleEvent(e) {
|
||||
var me = this;
|
||||
var options = me.options;
|
||||
var changed = false;
|
||||
|
||||
me._lastActive = me._lastActive || [];
|
||||
const me = this;
|
||||
const options = me.options;
|
||||
const lastActive = me._active || [];
|
||||
let changed = false;
|
||||
let active = [];
|
||||
|
||||
// Find Active Elements for tooltips
|
||||
if (e.type === 'mouseout') {
|
||||
me._active = [];
|
||||
} else {
|
||||
me._active = me._chart.getElementsAtEventForMode(e, options.mode, options);
|
||||
if (e.type !== 'mouseout') {
|
||||
active = me._chart.getElementsAtEventForMode(e, options.mode, options);
|
||||
if (options.reverse) {
|
||||
me._active.reverse();
|
||||
active.reverse();
|
||||
}
|
||||
}
|
||||
|
||||
// Remember Last Actives
|
||||
changed = !helpers._elementsEqual(me._active, me._lastActive);
|
||||
changed = !helpers._elementsEqual(active, lastActive);
|
||||
|
||||
// Only handle target event on tooltip change
|
||||
if (changed) {
|
||||
me._lastActive = me._active;
|
||||
me._active = active;
|
||||
|
||||
if (options.enabled || options.custom) {
|
||||
me._eventPosition = {
|
||||
@@ -992,7 +989,6 @@ class Tooltip extends Element {
|
||||
};
|
||||
|
||||
me.update(true);
|
||||
// me.pivot();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -153,7 +153,7 @@ export function drawPoint(ctx, style, radius, x, y, rotation) {
|
||||
* @private
|
||||
*/
|
||||
export function _isPointInArea(point, area) {
|
||||
var epsilon = 1e-6; // 1e-6 is margin in pixels for accumulated error.
|
||||
const epsilon = 0.5; // margin - to match rounded decimals
|
||||
|
||||
return point.x > area.left - epsilon && point.x < area.right + epsilon &&
|
||||
point.y > area.top - epsilon && point.y < area.bottom + epsilon;
|
||||
|
||||
@@ -773,6 +773,11 @@ describe('Chart.controllers.line', function() {
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
x: {
|
||||
offset: true
|
||||
}
|
||||
},
|
||||
elements: {
|
||||
point: {
|
||||
backgroundColor: 'rgb(100, 150, 200)',
|
||||
|
||||
@@ -1130,12 +1130,12 @@ describe('Chart', function() {
|
||||
var tooltip = chart.tooltip;
|
||||
|
||||
expect(chart.lastActive[0].element).toEqual(point);
|
||||
expect(tooltip._lastActive[0].element).toEqual(point);
|
||||
expect(tooltip._active[0].element).toEqual(point);
|
||||
|
||||
// Update and confirm tooltip is updated
|
||||
chart.update();
|
||||
expect(chart.lastActive[0].element).toEqual(point);
|
||||
expect(tooltip._lastActive[0].element).toEqual(point);
|
||||
expect(tooltip._active[0].element).toEqual(point);
|
||||
});
|
||||
|
||||
it ('should update the metadata', function() {
|
||||
|
||||
@@ -143,8 +143,8 @@ describe('Core.Interaction', function() {
|
||||
type: 'click',
|
||||
chart: chart,
|
||||
native: true, // needed otherwise things its a DOM event
|
||||
x: 0,
|
||||
y: 0
|
||||
x: chart.chartArea.left,
|
||||
y: chart.chartArea.top
|
||||
};
|
||||
|
||||
var elements = Chart.Interaction.modes.index(chart, evt, {intersect: false}).map(item => item.element);
|
||||
@@ -182,8 +182,8 @@ describe('Core.Interaction', function() {
|
||||
type: 'click',
|
||||
chart: chart,
|
||||
native: true, // needed otherwise things its a DOM event
|
||||
x: 0,
|
||||
y: 0
|
||||
x: chart.chartArea.left,
|
||||
y: chart.chartArea.top
|
||||
};
|
||||
|
||||
var elements = Chart.Interaction.modes.index(chart, evt, {axis: 'xy', intersect: false}).map(item => item.element);
|
||||
@@ -279,8 +279,8 @@ describe('Core.Interaction', function() {
|
||||
type: 'click',
|
||||
chart: chart,
|
||||
native: true, // needed otherwise things its a DOM event
|
||||
x: 0,
|
||||
y: 0
|
||||
x: chart.chartArea.left,
|
||||
y: chart.chartArea.top
|
||||
};
|
||||
|
||||
var elements = Chart.Interaction.modes.dataset(chart, evt, {axis: 'x', intersect: false});
|
||||
@@ -293,8 +293,8 @@ describe('Core.Interaction', function() {
|
||||
type: 'click',
|
||||
chart: chart,
|
||||
native: true, // needed otherwise things its a DOM event
|
||||
x: 0,
|
||||
y: 0
|
||||
x: chart.chartArea.left,
|
||||
y: chart.chartArea.top
|
||||
};
|
||||
|
||||
var elements = Chart.Interaction.modes.dataset(chart, evt, {axis: 'y', intersect: false});
|
||||
@@ -307,8 +307,8 @@ describe('Core.Interaction', function() {
|
||||
type: 'click',
|
||||
chart: chart,
|
||||
native: true, // needed otherwise things its a DOM event
|
||||
x: 0,
|
||||
y: 0
|
||||
x: chart.chartArea.left,
|
||||
y: chart.chartArea.top
|
||||
};
|
||||
|
||||
var elements = Chart.Interaction.modes.dataset(chart, evt, {intersect: false});
|
||||
@@ -348,8 +348,8 @@ describe('Core.Interaction', function() {
|
||||
type: 'click',
|
||||
chart: chart,
|
||||
native: true, // needed otherwise things its a DOM event
|
||||
x: 0,
|
||||
y: 0
|
||||
x: chart.chartArea.left,
|
||||
y: chart.chartArea.top
|
||||
};
|
||||
|
||||
// Nearest to 0,0 (top left) will be first point of dataset 2
|
||||
|
||||
@@ -70,7 +70,7 @@ describe('Core.Tooltip', function() {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
clientX: rect.left + point.x,
|
||||
clientY: 0
|
||||
clientY: rect.top + chart.chartArea.top + 5 // +5 to make tests work consistently
|
||||
});
|
||||
|
||||
// Manually trigger rather than having an async test
|
||||
|
||||
@@ -32,8 +32,8 @@ describe('Chart.helpers.canvas', function() {
|
||||
expect(isPointInArea({x: -1e-12, y: -1e-12}, area)).toBe(true);
|
||||
expect(isPointInArea({x: 512, y: 256}, area)).toBe(true);
|
||||
expect(isPointInArea({x: 512 + 1e-12, y: 256 + 1e-12}, area)).toBe(true);
|
||||
expect(isPointInArea({x: -1e-3, y: 0}, area)).toBe(false);
|
||||
expect(isPointInArea({x: 0, y: 256 + 1e-3}, area)).toBe(false);
|
||||
expect(isPointInArea({x: -0.5, y: 0}, area)).toBe(false);
|
||||
expect(isPointInArea({x: 0, y: 256.5}, area)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user