Skip to content

Commit 866a149

Browse files
authored
Merge pull request #3716 from plotly/replot-on-drag
Replot on drag
2 parents 0b4e549 + 51b74b5 commit 866a149

File tree

10 files changed

+474
-113
lines changed

10 files changed

+474
-113
lines changed

src/components/dragelement/index.js

+18-25
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,13 @@
66
* LICENSE file in the root directory of this source tree.
77
*/
88

9-
109
'use strict';
1110

1211
var mouseOffset = require('mouse-event-offset');
1312
var hasHover = require('has-hover');
1413
var supportsPassive = require('has-passive-events');
1514

16-
var Registry = require('../../registry');
17-
var Lib = require('../../lib');
18-
15+
var removeElement = require('../../lib').removeElement;
1916
var constants = require('../../plots/cartesian/constants');
2017
var interactConstants = require('../../constants/interactions');
2118

@@ -28,7 +25,6 @@ var unhover = require('./unhover');
2825
dragElement.unhover = unhover.wrapped;
2926
dragElement.unhoverRaw = unhover.raw;
3027

31-
3228
/**
3329
* Abstracts click & drag interactions
3430
*
@@ -106,8 +102,7 @@ dragElement.init = function init(options) {
106102

107103
if(!supportsPassive) {
108104
element.ontouchstart = onStart;
109-
}
110-
else {
105+
} else {
111106
if(element._ontouchstart) {
112107
element.removeEventListener('touchstart', element._ontouchstart);
113108
}
@@ -145,8 +140,7 @@ dragElement.init = function init(options) {
145140
if(newMouseDownTime - gd._mouseDownTime < DBLCLICKDELAY) {
146141
// in a click train
147142
numClicks += 1;
148-
}
149-
else {
143+
} else {
150144
// new click train
151145
numClicks = 1;
152146
gd._mouseDownTime = newMouseDownTime;
@@ -157,8 +151,7 @@ dragElement.init = function init(options) {
157151
if(hasHover && !rightClick) {
158152
dragCover = coverSlip();
159153
dragCover.style.cursor = window.getComputedStyle(element).cursor;
160-
}
161-
else if(!hasHover) {
154+
} else if(!hasHover) {
162155
// document acts as a dragcover for mobile, bc we can't create dragcover dynamically
163156
dragCover = document;
164157
cursor = window.getComputedStyle(document.documentElement).cursor;
@@ -191,12 +184,21 @@ dragElement.init = function init(options) {
191184
dragElement.unhover(gd);
192185
}
193186

194-
if(gd._dragged && options.moveFn && !rightClick) options.moveFn(dx, dy);
187+
if(gd._dragged && options.moveFn && !rightClick) {
188+
gd._dragdata = {
189+
element: element,
190+
dx: dx,
191+
dy: dy
192+
};
193+
options.moveFn(dx, dy);
194+
}
195195

196196
return;
197197
}
198198

199199
function onDone(e) {
200+
delete gd._dragdata;
201+
200202
if(options.dragmode !== false) {
201203
e.preventDefault();
202204
document.removeEventListener('mousemove', onMove);
@@ -207,9 +209,8 @@ dragElement.init = function init(options) {
207209
document.removeEventListener('touchend', onDone);
208210

209211
if(hasHover) {
210-
Lib.removeElement(dragCover);
211-
}
212-
else if(cursor) {
212+
removeElement(dragCover);
213+
} else if(cursor) {
213214
dragCover.documentElement.style.cursor = cursor;
214215
cursor = null;
215216
}
@@ -228,8 +229,7 @@ dragElement.init = function init(options) {
228229

229230
if(gd._dragged) {
230231
if(options.doneFn) options.doneFn();
231-
}
232-
else {
232+
} else {
233233
if(options.clickFn) options.clickFn(numClicks, initialEvent);
234234

235235
// If we haven't dragged, this should be a click. But because of the
@@ -258,10 +258,8 @@ dragElement.init = function init(options) {
258258
}
259259
}
260260

261-
finishDrag(gd);
262-
261+
gd._dragging = false;
263262
gd._dragged = false;
264-
265263
return;
266264
}
267265
};
@@ -286,11 +284,6 @@ function coverSlip() {
286284

287285
dragElement.coverSlip = coverSlip;
288286

289-
function finishDrag(gd) {
290-
gd._dragging = false;
291-
if(gd._replotPending) Registry.call('plot', gd);
292-
}
293-
294287
function pointerOffset(e) {
295288
return mouseOffset(
296289
e.changedTouches ? e.changedTouches[0] : e,

src/plot_api/plot_api.js

+13-27
Original file line numberDiff line numberDiff line change
@@ -135,18 +135,8 @@ exports.plot = function(gd, data, layout, config) {
135135
gd.empty = false;
136136
}
137137

138-
if(!gd.layout || graphWasEmpty) gd.layout = helpers.cleanLayout(layout);
139-
140-
// if the user is trying to drag the axes, allow new data and layout
141-
// to come in but don't allow a replot.
142-
if(gd._dragging && !gd._transitioning) {
143-
// signal to drag handler that after everything else is done
144-
// we need to replot, because something has changed
145-
gd._replotPending = true;
146-
return Promise.reject();
147-
} else {
148-
// we're going ahead with a replot now
149-
gd._replotPending = false;
138+
if(!gd.layout || graphWasEmpty) {
139+
gd.layout = helpers.cleanLayout(layout);
150140
}
151141

152142
Plots.supplyDefaults(gd);
@@ -195,7 +185,7 @@ exports.plot = function(gd, data, layout, config) {
195185
if(gd._context.responsive) {
196186
if(!gd._responsiveChartHandler) {
197187
// Keep a reference to the resize handler to purge it down the road
198-
gd._responsiveChartHandler = function() {Plots.resize(gd);};
188+
gd._responsiveChartHandler = function() { Plots.resize(gd); };
199189

200190
// Listen to window resize
201191
window.addEventListener('resize', gd._responsiveChartHandler);
@@ -242,10 +232,10 @@ exports.plot = function(gd, data, layout, config) {
242232
return 'gl-canvas gl-canvas-' + d.key.replace('Layer', '');
243233
})
244234
.style({
245-
'position': 'absolute',
246-
'top': 0,
247-
'left': 0,
248-
'overflow': 'visible',
235+
position: 'absolute',
236+
top: 0,
237+
left: 0,
238+
overflow: 'visible',
249239
'pointer-events': 'none'
250240
});
251241
}
@@ -384,6 +374,7 @@ exports.plot = function(gd, data, layout, config) {
384374
initInteractions,
385375
Plots.addLinks,
386376
Plots.rehover,
377+
Plots.redrag,
387378
// TODO: doAutoMargin is only needed here for axis automargin, which
388379
// happens outside of marginPushers where all the other automargins are
389380
// calculated. Would be much better to separate margin calculations from
@@ -1402,7 +1393,7 @@ function restyle(gd, astr, val, _traces) {
14021393
seq.push(emitAfterPlot);
14031394
}
14041395

1405-
seq.push(Plots.rehover);
1396+
seq.push(Plots.rehover, Plots.redrag);
14061397

14071398
Queue.add(gd,
14081399
restyle, [gd, specs.undoit, specs.traces],
@@ -1911,7 +1902,7 @@ function relayout(gd, astr, val) {
19111902
seq.push(emitAfterPlot);
19121903
}
19131904

1914-
seq.push(Plots.rehover);
1905+
seq.push(Plots.rehover, Plots.redrag);
19151906

19161907
Queue.add(gd,
19171908
relayout, [gd, specs.undoit],
@@ -1992,13 +1983,8 @@ function addAxRangeSequence(seq, rangesAltered) {
19921983
return Axes.draw(gd, 'redraw');
19931984
};
19941985

1995-
var _clearSelect = function(gd) {
1996-
var zoomlayer = gd._fullLayout._zoomlayer;
1997-
if(zoomlayer) clearSelect(zoomlayer);
1998-
};
1999-
20001986
seq.push(
2001-
_clearSelect,
1987+
clearSelect,
20021988
subroutines.doAutoRangeAndConstraints,
20031989
drawAxes,
20041990
subroutines.drawData,
@@ -2449,7 +2435,7 @@ function update(gd, traceUpdate, layoutUpdate, _traces) {
24492435
seq.push(emitAfterPlot);
24502436
}
24512437

2452-
seq.push(Plots.rehover);
2438+
seq.push(Plots.rehover, Plots.redrag);
24532439

24542440
Queue.add(gd,
24552441
update, [gd, restyleSpecs.undoit, relayoutSpecs.undoit, restyleSpecs.traces],
@@ -2852,7 +2838,7 @@ exports.react = function(gd, data, layout, config) {
28522838
seq.push(emitAfterPlot);
28532839
}
28542840

2855-
seq.push(Plots.rehover);
2841+
seq.push(Plots.rehover, Plots.redrag);
28562842

28572843
plotDone = Lib.syncOrAsync(seq, gd);
28582844
if(!plotDone || !plotDone.then) plotDone = Promise.resolve(gd);

src/plots/cartesian/dragbox.js

+23-8
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
* LICENSE file in the root directory of this source tree.
77
*/
88

9-
109
'use strict';
1110

1211
var d3 = require('d3');
@@ -38,7 +37,6 @@ var constants = require('./constants');
3837
var MINDRAG = constants.MINDRAG;
3938
var MINZOOM = constants.MINZOOM;
4039

41-
4240
// flag for showing "doubleclick to zoom out" only at the beginning
4341
var SHOWZOOMOUTTIP = true;
4442

@@ -216,13 +214,30 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
216214
}
217215
}
218216
}
217+
218+
gd._fullLayout._redrag = function() {
219+
var dragDataNow = gd._dragdata;
220+
221+
if(dragDataNow && dragDataNow.element === dragger) {
222+
var dragModeNow = gd._fullLayout.dragmode;
223+
224+
if(!isSelectOrLasso(dragModeNow)) {
225+
recomputeAxisLists();
226+
updateSubplots([0, 0, pw, ph]);
227+
dragOptions.moveFn(dragDataNow.dx, dragDataNow.dy);
228+
}
229+
230+
// TODO should we try to "re-select" under select/lasso modes?
231+
// probably best to wait for https://github.com/plotly/plotly.js/issues/1851
232+
}
233+
};
219234
};
220235

221236
function clearAndResetSelect() {
222237
// clear selection polygon cache (if any)
223238
dragOptions.plotinfo.selection = false;
224239
// clear selection outlines
225-
clearSelect(zoomlayer);
240+
clearSelect(gd);
226241
}
227242

228243
function clickFn(numClicks, evt) {
@@ -587,8 +602,8 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
587602
else if(yActive === 's') dy = dz(yaxes, 0, -dy);
588603
else if(!yActive) dy = 0;
589604

590-
var x0 = (xActive === 'w') ? dx : 0;
591-
var y0 = (yActive === 'n') ? dy : 0;
605+
var xStart = (xActive === 'w') ? dx : 0;
606+
var yStart = (yActive === 'n') ? dy : 0;
592607

593608
if(links.isSubplotConstrained) {
594609
var i;
@@ -600,21 +615,21 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
600615
scaleZoom(xaxes[i], 1 - dy / ph);
601616
}
602617
dx = dy * pw / ph;
603-
x0 = dx / 2;
618+
xStart = dx / 2;
604619
}
605620
if(!yActive && xActive.length === 1) {
606621
for(i = 0; i < yaxes.length; i++) {
607622
yaxes[i].range = yaxes[i]._r.slice();
608623
scaleZoom(yaxes[i], 1 - dx / pw);
609624
}
610625
dy = dx * ph / pw;
611-
y0 = dy / 2;
626+
yStart = dy / 2;
612627
}
613628
}
614629

615630
updateMatchedAxRange('x');
616631
updateMatchedAxRange('y');
617-
updateSubplots([x0, y0, pw - dx, ph - dy]);
632+
updateSubplots([xStart, yStart, pw - dx, ph - dy]);
618633
ticksAndAnnotations();
619634
}
620635

src/plots/cartesian/select.js

+15-10
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ var throttle = require('../../lib/throttle');
2020
var makeEventData = require('../../components/fx/helpers').makeEventData;
2121
var getFromId = require('./axis_ids').getFromId;
2222
var clearGlCanvases = require('../../lib/clear_gl_canvases');
23+
2324
var redrawReglTraces = require('../../plot_api/subroutines').redrawReglTraces;
2425

2526
var constants = require('./constants');
@@ -465,14 +466,14 @@ function multiTester(list) {
465466

466467
function coerceSelectionsCache(evt, gd, dragOptions) {
467468
var fullLayout = gd._fullLayout;
468-
var zoomLayer = fullLayout._zoomlayer;
469469
var plotinfo = dragOptions.plotinfo;
470470

471471
var selectingOnSameSubplot = (
472-
fullLayout._lastSelectedSubplot &&
473-
fullLayout._lastSelectedSubplot === plotinfo.id
472+
fullLayout._lastSelectedSubplot &&
473+
fullLayout._lastSelectedSubplot === plotinfo.id
474474
);
475475
var hasModifierKey = evt.shiftKey || evt.altKey;
476+
476477
if(selectingOnSameSubplot && hasModifierKey &&
477478
(plotinfo.selection && plotinfo.selection.selectionDefs) && !dragOptions.selectionDefs) {
478479
// take over selection definitions from prev mode, if any
@@ -484,7 +485,7 @@ function coerceSelectionsCache(evt, gd, dragOptions) {
484485

485486
// clear selection outline when selecting a different subplot
486487
if(!selectingOnSameSubplot) {
487-
clearSelect(zoomLayer);
488+
clearSelect(gd);
488489
fullLayout._lastSelectedSubplot = plotinfo.id;
489490
}
490491
}
@@ -669,7 +670,7 @@ function updateSelectedState(gd, searchTraces, eventData) {
669670
// before anything else, update preGUI if necessary
670671
for(i = 0; i < searchTraces.length; i++) {
671672
var fullInputTrace = searchTraces[i].cd[0].trace._fullInput;
672-
var tracePreGUI = gd._fullLayout._tracePreGUI[fullInputTrace.uid];
673+
var tracePreGUI = gd._fullLayout._tracePreGUI[fullInputTrace.uid] || {};
673674
if(tracePreGUI.selectedpoints === undefined) {
674675
tracePreGUI.selectedpoints = fullInputTrace._input.selectedpoints || null;
675676
}
@@ -774,11 +775,15 @@ function fillSelectionItem(selection, searchInfo) {
774775
return selection;
775776
}
776777

777-
function clearSelect(zoomlayer) {
778-
// until we get around to persistent selections, remove the outline
779-
// here. The selection itself will be removed when the plot redraws
780-
// at the end.
781-
zoomlayer.selectAll('.select-outline').remove();
778+
// until we get around to persistent selections, remove the outline
779+
// here. The selection itself will be removed when the plot redraws
780+
// at the end.
781+
function clearSelect(gd) {
782+
var fullLayout = gd._fullLayout || {};
783+
var zoomlayer = fullLayout._zoomlayer;
784+
if(zoomlayer) {
785+
zoomlayer.selectAll('.select-outline').remove();
786+
}
782787
}
783788

784789
module.exports = {

0 commit comments

Comments
 (0)