Skip to content

Commit ceac510

Browse files
authored
Merge pull request #3422 from plotly/scrollzoom-flaglist
Make scrollZoom config option a flaglist
2 parents f9cfadb + 09f1af5 commit ceac510

File tree

12 files changed

+238
-35
lines changed

12 files changed

+238
-35
lines changed

src/plot_api/plot_api.js

+19
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,25 @@ function setPlotContext(gd, config) {
507507
// Check if gd has a specified widht/height to begin with
508508
context._hasZeroHeight = context._hasZeroHeight || gd.clientHeight === 0;
509509
context._hasZeroWidth = context._hasZeroWidth || gd.clientWidth === 0;
510+
511+
// fill context._scrollZoom helper to help manage scrollZoom flaglist
512+
var szIn = context.scrollZoom;
513+
var szOut = context._scrollZoom = {};
514+
if(szIn === true) {
515+
szOut.cartesian = 1;
516+
szOut.gl3d = 1;
517+
szOut.geo = 1;
518+
szOut.mapbox = 1;
519+
} else if(typeof szIn === 'string') {
520+
var parts = szIn.split('+');
521+
for(i = 0; i < parts.length; i++) {
522+
szOut[parts[i]] = 1;
523+
}
524+
} else if(szIn !== false) {
525+
szOut.gl3d = 1;
526+
szOut.geo = 1;
527+
szOut.mapbox = 1;
528+
}
510529
}
511530

512531
function plotLegacyPolar(gd, data, layout) {

src/plot_api/plot_config.js

+9-4
Original file line numberDiff line numberDiff line change
@@ -145,11 +145,16 @@ var configAttributes = {
145145
},
146146

147147
scrollZoom: {
148-
valType: 'boolean',
149-
dflt: false,
148+
valType: 'flaglist',
149+
flags: ['cartesian', 'gl3d', 'geo', 'mapbox'],
150+
extras: [true, false],
151+
dflt: 'gl3d+geo+mapbox',
150152
description: [
151-
'Determines whether mouse wheel or two-finger scroll zooms is',
152-
'enable. Has an effect only on cartesian subplots.'
153+
'Determines whether mouse wheel or two-finger scroll zooms is enable.',
154+
'Turned on by default for gl3d, geo and mapbox subplots',
155+
'(as these subplot types do not have zoombox via pan),',
156+
'but turned off by default for cartesian subplots.',
157+
'Set `scrollZoom` to *false* to disable scrolling for all subplots.'
153158
].join(' ')
154159
},
155160
doubleClick: {

src/plots/cartesian/dragbox.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
417417
// deactivate mousewheel scrolling on embedded graphs
418418
// devs can override this with layout._enablescrollzoom,
419419
// but _ ensures this setting won't leave their page
420-
if(!gd._context.scrollZoom && !gd._fullLayout._enablescrollzoom) {
420+
if(!gd._context._scrollZoom.cartesian && !gd._fullLayout._enablescrollzoom) {
421421
return;
422422
}
423423

src/plots/geo/geo.js

+3
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,9 @@ proto.updateFx = function(fullLayout, geoLayout) {
420420
bgRect.node().onmousedown = null;
421421
bgRect.call(createGeoZoom(_this, geoLayout));
422422
bgRect.on('dblclick.zoom', zoomReset);
423+
if(!gd._context._scrollZoom.geo) {
424+
bgRect.on('wheel.zoom', null);
425+
}
423426
}
424427
else if(dragMode === 'select' || dragMode === 'lasso') {
425428
bgRect.on('.zoom', null);

src/plots/gl2d/scene2d.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ function Scene2D(options, fullLayout) {
3838
this.pixelRatio = options.plotGlPixelRatio || window.devicePixelRatio;
3939
this.id = options.id;
4040
this.staticPlot = !!options.staticPlot;
41-
this.scrollZoom = this.graphDiv._context.scrollZoom;
41+
this.scrollZoom = this.graphDiv._context._scrollZoom.cartesian;
4242

4343
this.fullData = null;
4444
this.updateRefs(fullLayout);

src/plots/gl3d/camera.js

+3
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ function createCamera(element, options) {
4848

4949
var camera = {
5050
keyBindingMode: 'rotate',
51+
enableWheel: true,
5152
view: view,
5253
element: element,
5354
delay: options.delay || 16,
@@ -257,7 +258,9 @@ function createCamera(element, options) {
257258
}
258259

259260
camera.wheelListener = mouseWheel(element, function(dx, dy) {
261+
// TODO remove now that we can disable scroll via scrollZoom?
260262
if(camera.keyBindingMode === false) return;
263+
if(!camera.enableWheel) return;
261264

262265
var flipX = camera.flipX ? 1 : -1;
263266
var flipY = camera.flipY ? 1 : -1;

src/plots/gl3d/scene.js

+10-4
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,15 @@ function initializeGLPlot(scene, canvas, gl) {
227227
scene.graphDiv.emit('plotly_relayout', update);
228228
};
229229

230-
scene.glplot.canvas.addEventListener('mouseup', relayoutCallback.bind(null, scene));
231-
scene.glplot.canvas.addEventListener('wheel', relayoutCallback.bind(null, scene), passiveSupported ? {passive: false} : false);
230+
scene.glplot.canvas.addEventListener('mouseup', function() {
231+
relayoutCallback(scene);
232+
});
233+
234+
scene.glplot.canvas.addEventListener('wheel', function() {
235+
if(gd._context._scrollZoom.gl3d) {
236+
relayoutCallback(scene);
237+
}
238+
}, passiveSupported ? {passive: false} : false);
232239

233240
if(!scene.staticMode) {
234241
scene.glplot.canvas.addEventListener('webglcontextlost', function(event) {
@@ -385,7 +392,6 @@ function computeTraceBounds(scene, trace, bounds) {
385392
}
386393

387394
proto.plot = function(sceneData, fullLayout, layout) {
388-
389395
// Save parameters
390396
this.plotArgs = [sceneData, fullLayout, layout];
391397

@@ -412,6 +418,7 @@ proto.plot = function(sceneData, fullLayout, layout) {
412418
// Update camera and camera mode
413419
this.setCamera(fullSceneLayout.camera);
414420
this.updateFx(fullSceneLayout.dragmode, fullSceneLayout.hovermode);
421+
this.camera.enableWheel = this.graphDiv._context._scrollZoom.gl3d;
415422

416423
// Update scene
417424
this.glplot.update({});
@@ -776,7 +783,6 @@ proto.updateFx = function(dragmode, hovermode) {
776783
fullCamera.up = zUp;
777784
Lib.nestedProperty(layout, attr).set(zUp);
778785
} else {
779-
780786
// none rotation modes [pan or zoom]
781787
camera.keyBindingMode = dragmode;
782788
}

src/plots/mapbox/mapbox.js

+6
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,12 @@ proto.updateMap = function(calcData, fullLayout, resolve, reject) {
258258
self.updateLayout(fullLayout);
259259
self.resolveOnRender(resolve);
260260
}
261+
262+
if(this.gd._context._scrollZoom.mapbox) {
263+
map.scrollZoom.enable();
264+
} else {
265+
map.scrollZoom.disable();
266+
}
261267
};
262268

263269
proto.updateData = function(calcData) {

test/jasmine/tests/config_test.js

+59
Original file line numberDiff line numberDiff line change
@@ -760,4 +760,63 @@ describe('config argument', function() {
760760
.then(done);
761761
});
762762
});
763+
764+
describe('scrollZoom:', function() {
765+
var gd;
766+
767+
beforeEach(function() { gd = createGraphDiv(); });
768+
769+
afterEach(destroyGraphDiv);
770+
771+
function plot(config) {
772+
return Plotly.plot(gd, [], {}, config);
773+
}
774+
775+
it('should fill in scrollZoom default', function(done) {
776+
plot(undefined).then(function() {
777+
expect(gd._context.scrollZoom).toBe('gl3d+geo+mapbox');
778+
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, mapbox: 1});
779+
expect(gd._context._scrollZoom.cartesian).toBe(undefined, 'no cartesian!');
780+
})
781+
.catch(failTest)
782+
.then(done);
783+
});
784+
785+
it('should fill in blank scrollZoom value', function(done) {
786+
plot({scrollZoom: null}).then(function() {
787+
expect(gd._context.scrollZoom).toBe(null);
788+
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, mapbox: 1});
789+
expect(gd._context._scrollZoom.cartesian).toBe(undefined, 'no cartesian!');
790+
})
791+
.catch(failTest)
792+
.then(done);
793+
});
794+
795+
it('should honor scrollZoom:true', function(done) {
796+
plot({scrollZoom: true}).then(function() {
797+
expect(gd._context.scrollZoom).toBe(true);
798+
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, cartesian: 1, mapbox: 1});
799+
})
800+
.catch(failTest)
801+
.then(done);
802+
});
803+
804+
it('should honor scrollZoom:false', function(done) {
805+
plot({scrollZoom: false}).then(function() {
806+
expect(gd._context.scrollZoom).toBe(false);
807+
expect(gd._context._scrollZoom).toEqual({});
808+
})
809+
.catch(failTest)
810+
.then(done);
811+
});
812+
813+
it('should honor scrollZoom flaglist', function(done) {
814+
plot({scrollZoom: 'mapbox+cartesian'}).then(function() {
815+
expect(gd._context.scrollZoom).toBe('mapbox+cartesian');
816+
expect(gd._context._scrollZoom).toEqual({mapbox: 1, cartesian: 1});
817+
})
818+
.catch(failTest)
819+
.then(done);
820+
});
821+
});
763822
});

test/jasmine/tests/geo_test.js

+48
Original file line numberDiff line numberDiff line change
@@ -1961,4 +1961,52 @@ describe('Test geo zoom/pan/drag interactions:', function() {
19611961
.catch(failTest)
19621962
.then(done);
19631963
});
1964+
1965+
it('should respect scrollZoom config option', function(done) {
1966+
var fig = Lib.extendDeep({}, require('@mocks/geo_winkel-tripel'));
1967+
fig.layout.width = 700;
1968+
fig.layout.height = 500;
1969+
fig.layout.dragmode = 'pan';
1970+
1971+
function _assert(step, attr, proj, eventKeys) {
1972+
var msg = '[' + step + '] ';
1973+
1974+
var geoLayout = gd._fullLayout.geo;
1975+
var scale = geoLayout.projection.scale;
1976+
expect(scale).toBeCloseTo(attr[0], 1, msg + 'zoom');
1977+
1978+
var geo = geoLayout._subplot;
1979+
var _scale = geo.projection.scale();
1980+
expect(_scale).toBeCloseTo(proj[0], 0, msg + 'scale');
1981+
1982+
assertEventData(msg, eventKeys);
1983+
}
1984+
1985+
plot(fig)
1986+
.then(function() {
1987+
_assert('base', [1], [101.9], undefined);
1988+
})
1989+
.then(function() { return scroll([200, 250], [-200, -200]); })
1990+
.then(function() {
1991+
_assert('with scroll enable (by default)',
1992+
[1.3], [134.4],
1993+
['geo.projection.rotation.lon', 'geo.center.lon', 'geo.center.lat', 'geo.projection.scale']
1994+
);
1995+
})
1996+
.then(function() { return Plotly.plot(gd, [], {}, {scrollZoom: false}); })
1997+
.then(function() { return scroll([200, 250], [-200, -200]); })
1998+
.then(function() {
1999+
_assert('with scrollZoom:false', [1.3], [134.4], undefined);
2000+
})
2001+
.then(function() { return Plotly.plot(gd, [], {}, {scrollZoom: 'geo'}); })
2002+
.then(function() { return scroll([200, 250], [-200, -200]); })
2003+
.then(function() {
2004+
_assert('with scrollZoom:geo',
2005+
[1.74], [177.34],
2006+
['geo.projection.rotation.lon', 'geo.center.lon', 'geo.center.lat', 'geo.projection.scale']
2007+
);
2008+
})
2009+
.catch(failTest)
2010+
.then(done);
2011+
});
19642012
});

test/jasmine/tests/gl3d_plot_interact_test.js

+34-25
Original file line numberDiff line numberDiff line change
@@ -1097,6 +1097,11 @@ describe('Test gl3d drag and wheel interactions', function() {
10971097
}
10981098
};
10991099

1100+
function _assertAndReset(cnt) {
1101+
expect(relayoutCallback).toHaveBeenCalledTimes(cnt);
1102+
relayoutCallback.calls.reset();
1103+
}
1104+
11001105
Plotly.plot(gd, mock)
11011106
.then(function() {
11021107
relayoutCallback = jasmine.createSpy('relayoutCallback');
@@ -1115,48 +1120,32 @@ describe('Test gl3d drag and wheel interactions', function() {
11151120
return scroll(sceneTarget);
11161121
})
11171122
.then(function() {
1118-
expect(relayoutCallback).toHaveBeenCalledTimes(1);
1119-
relayoutCallback.calls.reset();
1120-
1123+
_assertAndReset(1);
11211124
return scroll(sceneTarget2);
11221125
})
11231126
.then(function() {
1124-
expect(relayoutCallback).toHaveBeenCalledTimes(1);
1125-
relayoutCallback.calls.reset();
1126-
1127+
_assertAndReset(1);
11271128
return drag(sceneTarget2, [0, 0], [100, 100]);
11281129
})
11291130
.then(function() {
1130-
expect(relayoutCallback).toHaveBeenCalledTimes(1);
1131-
relayoutCallback.calls.reset();
1132-
1131+
_assertAndReset(1);
11331132
return drag(sceneTarget, [0, 0], [100, 100]);
11341133
})
11351134
.then(function() {
1136-
expect(relayoutCallback).toHaveBeenCalledTimes(1);
1137-
relayoutCallback.calls.reset();
1138-
1139-
return Plotly.relayout(gd, {
1140-
'scene.dragmode': false,
1141-
'scene2.dragmode': false
1142-
});
1135+
_assertAndReset(1);
1136+
return Plotly.relayout(gd, {'scene.dragmode': false, 'scene2.dragmode': false});
11431137
})
11441138
.then(function() {
1145-
expect(relayoutCallback).toHaveBeenCalledTimes(1);
1146-
relayoutCallback.calls.reset();
1147-
1139+
_assertAndReset(1);
11481140
return drag(sceneTarget, [0, 0], [100, 100]);
11491141
})
11501142
.then(function() {
11511143
return drag(sceneTarget2, [0, 0], [100, 100]);
11521144
})
11531145
.then(function() {
1154-
expect(relayoutCallback).toHaveBeenCalledTimes(0);
1146+
_assertAndReset(0);
11551147

1156-
return Plotly.relayout(gd, {
1157-
'scene.dragmode': 'orbit',
1158-
'scene2.dragmode': 'turntable'
1159-
});
1148+
return Plotly.relayout(gd, {'scene.dragmode': 'orbit', 'scene2.dragmode': 'turntable'});
11601149
})
11611150
.then(function() {
11621151
expect(relayoutCallback).toHaveBeenCalledTimes(1);
@@ -1168,7 +1157,27 @@ describe('Test gl3d drag and wheel interactions', function() {
11681157
return drag(sceneTarget2, [0, 0], [100, 100]);
11691158
})
11701159
.then(function() {
1171-
expect(relayoutCallback).toHaveBeenCalledTimes(2);
1160+
_assertAndReset(2);
1161+
return Plotly.plot(gd, [], {}, {scrollZoom: false});
1162+
})
1163+
.then(function() {
1164+
return scroll(sceneTarget);
1165+
})
1166+
.then(function() {
1167+
return scroll(sceneTarget2);
1168+
})
1169+
.then(function() {
1170+
_assertAndReset(0);
1171+
return Plotly.plot(gd, [], {}, {scrollZoom: 'gl3d'});
1172+
})
1173+
.then(function() {
1174+
return scroll(sceneTarget);
1175+
})
1176+
.then(function() {
1177+
return scroll(sceneTarget2);
1178+
})
1179+
.then(function() {
1180+
_assertAndReset(2);
11721181
})
11731182
.catch(failTest)
11741183
.then(done);

0 commit comments

Comments
 (0)