]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Limit interactions to chartArea (+/-0.5px) (#6943)
authorJukka Kurkela <jukka.kurkela@gmail.com>
Sat, 11 Jan 2020 23:10:32 +0000 (01:10 +0200)
committerEvert Timberg <evert.timberg+github@gmail.com>
Sat, 11 Jan 2020 23:10:32 +0000 (18:10 -0500)
Limit interactions to chartArea (+/-0.5px)

docs/getting-started/v3-migration.md
src/controllers/controller.horizontalBar.js
src/core/core.interaction.js
src/core/core.tooltip.js
src/helpers/helpers.canvas.js
test/specs/controller.line.tests.js
test/specs/core.controller.tests.js
test/specs/core.interaction.tests.js
test/specs/core.tooltip.tests.js
test/specs/helpers.canvas.tests.js

index 5f8cea12636bffdc9ab3480f61cb9bea5eb0408b..f280cd2b5d1581c1d4b7ba47fba99528e9aaaa77 100644 (file)
@@ -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
 
index ca04f3befbd74da1b3476aabb8dcbced8ed33166..449cf666521c49cd14db10f1164d3f4bfb97f82e 100644 (file)
@@ -12,7 +12,8 @@ defaults._set('horizontalBar', {
        scales: {
                x: {
                        type: 'linear',
-                       position: 'bottom'
+                       position: 'bottom',
+                       beginAtZero: true
                },
                y: {
                        type: 'category',
index 2224d72429e74aa12aa515744808e9f927deb00d..3afce48fccc11119a12786b9cf801cfa38f8bd17 100644 (file)
@@ -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;
index bbd7ae1326c830fd198c11005682a576d64a6fca..0300d6af1a944a3cae78201f314e09265de578bc 100644 (file)
@@ -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();
                        }
                }
 
index 481eb6c0122b7858879c3fc5398764549a658119..64f38db48265d0cfdd4a6fe6dc86d48caa4e5267 100644 (file)
@@ -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;
index 383a0443a63baa6856957e905192e90ee4f8e577..09c148049d7f36822b5d92b5f44cd5ae1f286a65 100644 (file)
@@ -773,6 +773,11 @@ describe('Chart.controllers.line', function() {
                                        }]
                                },
                                options: {
+                                       scales: {
+                                               x: {
+                                                       offset: true
+                                               }
+                                       },
                                        elements: {
                                                point: {
                                                        backgroundColor: 'rgb(100, 150, 200)',
index 58ea472793d92ef99c2963e6fe043ad8bd0423d5..0053ca91725f19739fe2068c883db35c198b5b47 100644 (file)
@@ -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() {
index 8eae2e9d7802d01a90149815ef2487f38807fac9..ea892c8176e999eb7dcf8144643d3214e0e65329 100644 (file)
@@ -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
index 01c5b303c381778d6c6b80c9e31c7f65035ef4f0..27145e543fe304642f7cc2e37ca716333b41ac25 100644 (file)
@@ -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
index 3a5fbb836638a55c88d48e3675a1fc83b326d20b..b355464430cd2598bc3c8172f7a3f21752279d3f 100644 (file)
@@ -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);
                });
        });
 });