Skip to content

Commit 09ff6d7

Browse files
authored
Merge pull request #1304 from plotly/graph-interact-cache-event
Cache the last mousemove event
2 parents f334c38 + 554d7ed commit 09ff6d7

File tree

4 files changed

+120
-13
lines changed

4 files changed

+120
-13
lines changed

src/plot_api/plot_api.js

+19-10
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ Plotly.plot = function(gd, data, layout, config) {
337337
Registry.getComponentMethod('updatemenus', 'draw')(gd);
338338
}
339339

340-
Lib.syncOrAsync([
340+
var seq = [
341341
Plots.previousPromises,
342342
addFrames,
343343
drawFramework,
@@ -347,8 +347,11 @@ Plotly.plot = function(gd, data, layout, config) {
347347
subroutines.layoutStyles,
348348
drawAxes,
349349
drawData,
350-
finalDraw
351-
], gd);
350+
finalDraw,
351+
Plots.rehover
352+
];
353+
354+
Lib.syncOrAsync(seq, gd);
352355

353356
// even if everything we did was synchronous, return a promise
354357
// so that the caller doesn't care which route we took
@@ -1201,8 +1204,7 @@ Plotly.restyle = function restyle(gd, astr, val, traces) {
12011204

12021205
if(flags.fullReplot) {
12031206
seq.push(Plotly.plot);
1204-
}
1205-
else {
1207+
} else {
12061208
seq.push(Plots.previousPromises);
12071209

12081210
Plots.supplyDefaults(gd);
@@ -1211,6 +1213,8 @@ Plotly.restyle = function restyle(gd, astr, val, traces) {
12111213
if(flags.docolorbars) seq.push(subroutines.doColorBars);
12121214
}
12131215

1216+
seq.push(Plots.rehover);
1217+
12141218
Queue.add(gd,
12151219
restyle, [gd, specs.undoit, specs.traces],
12161220
restyle, [gd, specs.redoit, specs.traces]
@@ -1695,9 +1699,11 @@ Plotly.relayout = function relayout(gd, astr, val) {
16951699
}
16961700

16971701
var aobj = {};
1698-
if(typeof astr === 'string') aobj[astr] = val;
1699-
else if(Lib.isPlainObject(astr)) aobj = astr;
1700-
else {
1702+
if(typeof astr === 'string') {
1703+
aobj[astr] = val;
1704+
} else if(Lib.isPlainObject(astr)) {
1705+
aobj = astr;
1706+
} else {
17011707
Lib.warn('Relayout fail.', astr, val);
17021708
return Promise.reject();
17031709
}
@@ -1715,8 +1721,7 @@ Plotly.relayout = function relayout(gd, astr, val) {
17151721

17161722
if(flags.layoutReplot) {
17171723
seq.push(subroutines.layoutReplot);
1718-
}
1719-
else if(Object.keys(aobj).length) {
1724+
} else if(Object.keys(aobj).length) {
17201725
seq.push(Plots.previousPromises);
17211726
Plots.supplyDefaults(gd);
17221727

@@ -1727,6 +1732,8 @@ Plotly.relayout = function relayout(gd, astr, val) {
17271732
if(flags.docamera) seq.push(subroutines.doCamera);
17281733
}
17291734

1735+
seq.push(Plots.rehover);
1736+
17301737
Queue.add(gd,
17311738
relayout, [gd, specs.undoit],
17321739
relayout, [gd, specs.redoit]
@@ -2127,6 +2134,8 @@ Plotly.update = function update(gd, traceUpdate, layoutUpdate, traces) {
21272134
if(relayoutFlags.doCamera) seq.push(subroutines.doCamera);
21282135
}
21292136

2137+
seq.push(Plots.rehover);
2138+
21302139
Queue.add(gd,
21312140
update, [gd, restyleSpecs.undoit, relayoutSpecs.undoit, restyleSpecs.traces],
21322141
update, [gd, restyleSpecs.redoit, relayoutSpecs.redoit, restyleSpecs.traces]

src/plots/cartesian/graph_interact.js

+18-2
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,20 @@ fx.init = function(gd) {
114114
xa._length, ya._length, 'ns', 'ew');
115115

116116
maindrag.onmousemove = function(evt) {
117+
// This is on `gd._fullLayout`, *not* fullLayout because the reference
118+
// changes by the time this is called again.
119+
gd._fullLayout._rehover = function() {
120+
if(gd._fullLayout._hoversubplot === subplot) {
121+
fx.hover(gd, evt, subplot);
122+
}
123+
};
124+
117125
fx.hover(gd, evt, subplot);
118-
fullLayout._lasthover = maindrag;
119-
fullLayout._hoversubplot = subplot;
126+
127+
// Note that we have *not* used the cached fullLayout variable here
128+
// since that may be outdated when this is called as a callback later on
129+
gd._fullLayout._lasthover = maindrag;
130+
gd._fullLayout._hoversubplot = subplot;
120131
};
121132

122133
/*
@@ -129,6 +140,11 @@ fx.init = function(gd) {
129140
maindrag.onmouseout = function(evt) {
130141
if(gd._dragging) return;
131142

143+
// When the mouse leaves this maindrag, unset the hovered subplot.
144+
// This may cause problems if it leaves the subplot directly *onto*
145+
// another subplot, but that's a tiny corner case at the moment.
146+
gd._fullLayout._hoversubplot = null;
147+
132148
dragElement.unhover(gd, evt);
133149
};
134150

src/plots/plots.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -1934,7 +1934,7 @@ plots.transition = function(gd, data, layout, traces, frameOpts, transitionOpts)
19341934
}
19351935
}
19361936

1937-
var seq = [plots.previousPromises, interruptPreviousTransitions, prepareTransitions, executeTransitions];
1937+
var seq = [plots.previousPromises, interruptPreviousTransitions, prepareTransitions, plots.rehover, executeTransitions];
19381938

19391939
var transitionStarting = Lib.syncOrAsync(seq, gd);
19401940

@@ -2057,6 +2057,12 @@ plots.doCalcdata = function(gd, traces) {
20572057
}
20582058
};
20592059

2060+
plots.rehover = function(gd) {
2061+
if(gd._fullLayout._rehover) {
2062+
gd._fullLayout._rehover();
2063+
}
2064+
};
2065+
20602066
plots.generalUpdatePerTraceModule = function(subplot, subplotCalcData, subplotLayout) {
20612067
var traceHashOld = subplot.traceHash,
20622068
traceHash = {},

test/jasmine/tests/hover_label_test.js

+76
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ var destroyGraphDiv = require('../assets/destroy_graph_div');
1010
var mouseEvent = require('../assets/mouse_event');
1111
var click = require('../assets/click');
1212
var doubleClick = require('../assets/double_click');
13+
var fail = require('../assets/fail_test');
1314

1415
describe('hover info', function() {
1516
'use strict';
@@ -811,3 +812,78 @@ describe('hover on fill', function() {
811812
}).then(done);
812813
});
813814
});
815+
816+
describe('hover updates', function() {
817+
'use strict';
818+
819+
afterEach(destroyGraphDiv);
820+
821+
function assertLabelsCorrect(mousePos, labelPos, labelText) {
822+
return new Promise(function(resolve) {
823+
if(mousePos) {
824+
mouseEvent('mousemove', mousePos[0], mousePos[1]);
825+
}
826+
827+
setTimeout(function() {
828+
var hoverText = d3.selectAll('g.hovertext');
829+
if(labelPos) {
830+
expect(hoverText.size()).toEqual(1);
831+
expect(hoverText.text()).toEqual(labelText);
832+
833+
var transformParts = hoverText.attr('transform').split('(');
834+
expect(transformParts[0]).toEqual('translate');
835+
var transformCoords = transformParts[1].split(')')[0].split(',');
836+
expect(+transformCoords[0]).toBeCloseTo(labelPos[0], -1, labelText + ':x');
837+
expect(+transformCoords[1]).toBeCloseTo(labelPos[1], -1, labelText + ':y');
838+
} else {
839+
expect(hoverText.size()).toEqual(0);
840+
}
841+
842+
resolve();
843+
}, constants.HOVERMINTIME);
844+
});
845+
}
846+
847+
it('should update the labels on animation', function(done) {
848+
var mock = {
849+
data: [
850+
{x: [0.5], y: [0.5], showlegend: false},
851+
{x: [0], y: [0], showlegend: false},
852+
],
853+
layout: {
854+
margin: {t: 0, r: 0, b: 0, l: 0},
855+
width: 200,
856+
height: 200,
857+
xaxis: {range: [0, 1]},
858+
yaxis: {range: [0, 1]},
859+
}
860+
};
861+
862+
var gd = createGraphDiv();
863+
Plotly.plot(gd, mock).then(function() {
864+
// The label text gets concatenated together when queried. Such is life.
865+
return assertLabelsCorrect([100, 100], [103, 100], 'trace 00.5');
866+
}).then(function() {
867+
return Plotly.animate(gd, [{
868+
data: [{x: [0], y: [0]}, {x: [0.5], y: [0.5]}],
869+
traces: [0, 1],
870+
}], {frame: {redraw: false, duration: 0}});
871+
}).then(function() {
872+
// No mouse event this time. Just change the data and check the label.
873+
// Ditto on concatenation. This is "trace 1" + "0.5"
874+
return assertLabelsCorrect(null, [103, 100], 'trace 10.5');
875+
}).then(function() {
876+
// Restyle to move the point out of the window:
877+
return Plotly.relayout(gd, {'xaxis.range': [2, 3]});
878+
}).then(function() {
879+
// Assert label removed:
880+
return assertLabelsCorrect(null, null);
881+
}).then(function() {
882+
// Move back to the original xaxis range:
883+
return Plotly.relayout(gd, {'xaxis.range': [0, 1]});
884+
}).then(function() {
885+
// Assert label restored:
886+
return assertLabelsCorrect(null, [103, 100], 'trace 10.5');
887+
}).catch(fail).then(done);
888+
});
889+
});

0 commit comments

Comments
 (0)