Skip to content

Commit a6be166

Browse files
committed
Disable mouse interaction during finite-duration transition
1 parent 0f848ab commit a6be166

File tree

3 files changed

+61
-21
lines changed

3 files changed

+61
-21
lines changed

src/plot_api/plot_api.js

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ Plotly.plot = function(gd, data, layout, config) {
107107

108108
// if the user is trying to drag the axes, allow new data and layout
109109
// to come in but don't allow a replot.
110-
if(gd._dragging) {
110+
if(gd._dragging && !gd._transitioning) {
111111
// signal to drag handler that after everything else is done
112112
// we need to replot, because something has changed
113113
gd._replotPending = true;
@@ -235,6 +235,7 @@ Plotly.plot = function(gd, data, layout, config) {
235235
}
236236

237237
function doAutoRange() {
238+
if(gd._transitioning) return;
238239
var axList = Plotly.Axes.list(gd, '', true);
239240
for(var i = 0; i < axList.length; i++) {
240241
Plotly.Axes.doAutoRange(axList[i]);
@@ -2597,6 +2598,8 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
25972598
doCalcdata(gd);
25982599

25992600
ErrorBars.calc(gd);
2601+
2602+
return Promise.resolve();
26002603
}
26012604

26022605
function executeCallbacks(list) {
@@ -2618,6 +2621,16 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
26182621
var aborted = false;
26192622

26202623
function executeTransitions() {
2624+
// This flag is used to disabled things like autorange:
2625+
gd._transitioning = true;
2626+
2627+
// When instantaneous updates are coming through quickly, it's too much to simply disable
2628+
// all interaction, so store this flag so we can disambiguate whether mouse interactions
2629+
// should be fully disabled or not:
2630+
if(transitionConfig.duration > 0) {
2631+
gd._transitioningWithDuration = true;
2632+
}
2633+
26212634
gd._transitionData._interruptCallbacks.push(function() {
26222635
aborted = true;
26232636
});
@@ -2638,7 +2651,6 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
26382651
}
26392652

26402653
var traceTransitionConfig;
2641-
var hasTraceTransition = false;
26422654
var j;
26432655
var basePlotModules = fullLayout._basePlotModules;
26442656
var hasAxisTransition = false;
@@ -2671,12 +2683,7 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
26712683
}
26722684

26732685
// If nothing else creates a callback, then this will trigger the completion in the next tick:
2674-
setTimeout(makeCallback());
2675-
2676-
if(!hasAxisTransition && !hasTraceTransition) {
2677-
return false;
2678-
}
2679-
2686+
setTimeout(makeCallback('first'));
26802687
}
26812688

26822689
function completeTransition() {
@@ -2687,13 +2694,25 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
26872694
return Plotly.redraw(gd);
26882695
}
26892696
}).then(function() {
2697+
// Set transitioning false again once the redraw has occurred. This is used, for example,
2698+
// to prevent the trailing redraw from autoranging:
2699+
gd._transitioning = false;
2700+
gd._transitioningWithDuration = false;
2701+
26902702
gd.emit('plotly_transitioned', []);
26912703
});
26922704
}
26932705

26942706
function interruptPreviousTransitions() {
26952707
gd.emit('plotly_transitioninterrupted', []);
26962708

2709+
// If a transition is interrupted, set this to false. At the moment, the only thing that would
2710+
// interrupt a transition is another transition, so that it will momentarily be set to true
2711+
// again, but this determines whether autorange or dragbox work, so it's for the sake of
2712+
// cleanliness:
2713+
gd._transitioning = false;
2714+
gd._transtionWithDuration = false;
2715+
26972716
return executeCallbacks(gd._transitionData._interruptCallbacks);
26982717
}
26992718

@@ -2715,11 +2734,12 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
27152734

27162735
var seq = [Plots.previousPromises, interruptPreviousTransitions, prepareTransitions, executeTransitions];
27172736

2718-
var plotDone = Lib.syncOrAsync(seq, gd);
27192737

2720-
if(!plotDone || !plotDone.then) plotDone = Promise.resolve();
2738+
var transitionStarting = Lib.syncOrAsync(seq, gd);
27212739

2722-
return plotDone.then(function() {
2740+
if(!transitionStarting || !transitionStarting.then) transitionStarting = Promise.resolve();
2741+
2742+
return transitionStarting.then(function() {
27232743
gd.emit('plotly_transitioning', []);
27242744
return gd;
27252745
});

src/plots/cartesian/dragbox.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,10 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) {
224224
}
225225

226226
function zoomMove(dx0, dy0) {
227+
if(gd._transitioningWithDuration) {
228+
return false;
229+
}
230+
227231
var x1 = Math.max(0, Math.min(pw, dx0 + x0)),
228232
y1 = Math.max(0, Math.min(ph, dy0 + y0)),
229233
dx = Math.abs(x1 - x0),
@@ -380,15 +384,22 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) {
380384
fullLayout._plots[plotinfo.mainplot] : plotinfo;
381385

382386
function zoomWheel(e) {
383-
recomputeAxisLists();
384387
// deactivate mousewheel scrolling on embedded graphs
385388
// devs can override this with layout._enablescrollzoom,
386389
// but _ ensures this setting won't leave their page
387390
if(!gd._context.scrollZoom && !fullLayout._enablescrollzoom) {
388391
return;
389392
}
393+
394+
// If a transition is in progress, then disable any behavior:
395+
if(gd._transitioningWithDuration) {
396+
return Lib.pauseEvent(e);
397+
}
398+
390399
var pc = gd.querySelector('.plotly');
391400

401+
recomputeAxisLists();
402+
392403
// if the plot has scrollbars (more than a tiny excess)
393404
// disable scrollzoom too.
394405
if(pc.scrollHeight - pc.clientHeight > 10 ||
@@ -456,6 +467,11 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) {
456467

457468
// plotDrag: move the plot in response to a drag
458469
function plotDrag(dx, dy) {
470+
// If a transition is in progress, then disable any behavior:
471+
if(gd._transitioningWithDuration) {
472+
return;
473+
}
474+
459475
recomputeAxisLists();
460476

461477
function dragAxList(axList, pix) {
@@ -558,6 +574,8 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) {
558574
}
559575

560576
function doubleClick() {
577+
if(gd._transitioningWithDuration) return;
578+
561579
var doubleClickConfig = gd._context.doubleClick,
562580
axList = (xActive ? xa : []).concat(yActive ? ya : []),
563581
attrs = {};

src/traces/scatter/plot.js

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ var linkTraces = require('./link_traces');
2222
var polygonTester = require('../../lib/polygon').tester;
2323

2424
module.exports = function plot(gd, plotinfo, cdscatter, transitionConfig, makeOnCompleteCallback) {
25-
var i, uids, selection, join;
25+
var i, uids, selection, join, onComplete;
2626

2727
var scatterlayer = plotinfo.plot.select('g.scatterlayer');
2828

@@ -31,14 +31,6 @@ module.exports = function plot(gd, plotinfo, cdscatter, transitionConfig, makeOn
3131
var isFullReplot = !transitionConfig;
3232
var hasTransition = !!transitionConfig && transitionConfig.duration > 0;
3333

34-
var onComplete;
35-
if(makeOnCompleteCallback && hasTransition) {
36-
// If it was passed a callback to register completion, make a callback. If
37-
// this is created, then it must be executed on completion, otherwise the
38-
// pos-transition redraw will not execute:
39-
onComplete = makeOnCompleteCallback();
40-
}
41-
4234
selection = scatterlayer.selectAll('g.trace');
4335

4436
join = selection.data(cdscatter, function(d) {return d[0].trace.uid;});
@@ -71,11 +63,21 @@ module.exports = function plot(gd, plotinfo, cdscatter, transitionConfig, makeOn
7163
});
7264

7365
if(hasTransition) {
66+
if(makeOnCompleteCallback) {
67+
// If it was passed a callback to register completion, make a callback. If
68+
// this is created, then it must be executed on completion, otherwise the
69+
// pos-transition redraw will not execute:
70+
onComplete = makeOnCompleteCallback();
71+
}
72+
7473
var transition = d3.transition()
7574
.duration(transitionConfig.duration)
7675
.ease(transitionConfig.ease)
7776
.each('end', function() {
7877
onComplete && onComplete();
78+
})
79+
.each('interrupt', function() {
80+
onComplete && onComplete();
7981
});
8082

8183
transition.each(function() {

0 commit comments

Comments
 (0)