Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/plot_api/plot_api.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -507,6 +507,25 @@ function setPlotContext(gd, config) {
// Check if gd has a specified widht/height to begin with
context._hasZeroHeight = context._hasZeroHeight || gd.clientHeight === 0;
context._hasZeroWidth = context._hasZeroWidth || gd.clientWidth === 0;

// fill context._scrollZoom helper to help manage scrollZoom flaglist
var szIn = context.scrollZoom;
var szOut = context._scrollZoom = {};
if(szIn === true) {
szOut.cartesian = 1;
szOut.gl3d = 1;
szOut.geo = 1;
szOut.mapbox = 1;
} else if(typeof szIn === 'string') {
var parts = szIn.split('+');
for(i = 0; i < parts.length; i++) {
szOut[parts[i]] = 1;
Comment thread
archmoj marked this conversation as resolved.
}
} else if(szIn !== false) {
szOut.gl3d = 1;
szOut.geo = 1;
szOut.mapbox = 1;
}
}

function plotLegacyPolar(gd, data, layout) {
Expand Down
13 changes: 9 additions & 4 deletions src/plot_api/plot_config.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -145,11 +145,16 @@ var configAttributes = {
},

scrollZoom: {
valType: 'boolean',
dflt: false,
valType: 'flaglist',
flags: ['cartesian', 'gl3d', 'geo', 'mapbox'],
extras: [true, false],
dflt: 'gl3d+geo+mapbox',
description: [
'Determines whether mouse wheel or two-finger scroll zooms is',
'enable. Has an effect only on cartesian subplots.'
'Determines whether mouse wheel or two-finger scroll zooms is enable.',
'Turned on by default for gl3d, geo and mapbox subplots',
'(as these subplot types do not have zoombox via pan),',
'but turned off by default for cartesian subplots.',
'Set `scrollZoom` to *false* to disable scrolling for all subplots.'
].join(' ')
},
doubleClick: {
Expand Down
2 changes: 1 addition & 1 deletion src/plots/cartesian/dragbox.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -417,7 +417,7 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
// deactivate mousewheel scrolling on embedded graphs
// devs can override this with layout._enablescrollzoom,
// but _ ensures this setting won't leave their page
if(!gd._context.scrollZoom && !gd._fullLayout._enablescrollzoom) {
if(!gd._context._scrollZoom.cartesian && !gd._fullLayout._enablescrollzoom) {
return;
}

Expand Down
3 changes: 3 additions & 0 deletions src/plots/geo/geo.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -420,6 +420,9 @@ proto.updateFx = function(fullLayout, geoLayout) {
bgRect.node().onmousedown = null;
bgRect.call(createGeoZoom(_this, geoLayout));
bgRect.on('dblclick.zoom', zoomReset);
if(!gd._context._scrollZoom.geo) {
bgRect.on('wheel.zoom', null);
}
}
else if(dragMode === 'select' || dragMode === 'lasso') {
bgRect.on('.zoom', null);
Expand Down
2 changes: 1 addition & 1 deletion src/plots/gl2d/scene2d.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,7 +38,7 @@ function Scene2D(options, fullLayout) {
this.pixelRatio = options.plotGlPixelRatio || window.devicePixelRatio;
this.id = options.id;
this.staticPlot = !!options.staticPlot;
this.scrollZoom = this.graphDiv._context.scrollZoom;
this.scrollZoom = this.graphDiv._context._scrollZoom.cartesian;

this.fullData = null;
this.updateRefs(fullLayout);
Expand Down
3 changes: 3 additions & 0 deletions src/plots/gl3d/camera.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,6 +48,7 @@ function createCamera(element, options) {

var camera = {
keyBindingMode: 'rotate',
enableWheel: true,
Comment thread
archmoj marked this conversation as resolved.
view: view,
element: element,
delay: options.delay || 16,
Expand DownExpand Up@@ -257,7 +258,9 @@ function createCamera(element, options) {
}

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

var flipX = camera.flipX ? 1 : -1;
var flipY = camera.flipY ? 1 : -1;
Expand Down
14 changes: 10 additions & 4 deletions src/plots/gl3d/scene.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -227,8 +227,15 @@ function initializeGLPlot(scene, canvas, gl) {
scene.graphDiv.emit('plotly_relayout', update);
};

scene.glplot.canvas.addEventListener('mouseup', relayoutCallback.bind(null, scene));
scene.glplot.canvas.addEventListener('wheel', relayoutCallback.bind(null, scene), passiveSupported ? {passive: false} : false);
scene.glplot.canvas.addEventListener('mouseup', function() {
relayoutCallback(scene);
});

scene.glplot.canvas.addEventListener('wheel', function() {
if(gd._context._scrollZoom.gl3d) {
relayoutCallback(scene);
}
}, passiveSupported ? {passive: false} : false);
Comment on lines +234 to +238

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've disabled scrollZoom in the plot config, but the mouse is still getting captured when scrolling by the canvas. Looks like it's because of the wheel event listener that's being registered here anyway. After I remove the wheel listener on scene element from chrome inspector, scrolling the page no longer gets captured by the canvas.

@etpinard, can we not add the listener at all if scrollzoom is disabled?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zfei thanks for the debugging. This comment is going to get lost here on a merged PR though, can you make a new issue for this? Or better yet a PR, looks like it's probably a pretty easy fix 😎

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @alexcjohnson.

I've never touched the Plotly codebase and haven't set up my mind to go through the learning curve yet. 😉

Will just leave it as an issue for now.


if(!scene.staticMode) {
scene.glplot.canvas.addEventListener('webglcontextlost', function(event) {
Expand DownExpand Up@@ -385,7 +392,6 @@ function computeTraceBounds(scene, trace, bounds) {
}

proto.plot = function(sceneData, fullLayout, layout) {

// Save parameters
this.plotArgs = [sceneData, fullLayout, layout];

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

// Update scene
this.glplot.update({});
Expand DownExpand Up@@ -776,7 +783,6 @@ proto.updateFx = function(dragmode, hovermode) {
fullCamera.up = zUp;
Lib.nestedProperty(layout, attr).set(zUp);
} else {

// none rotation modes [pan or zoom]
camera.keyBindingMode = dragmode;
}
Expand Down
6 changes: 6 additions & 0 deletions src/plots/mapbox/mapbox.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -258,6 +258,12 @@ proto.updateMap = function(calcData, fullLayout, resolve, reject) {
self.updateLayout(fullLayout);
self.resolveOnRender(resolve);
}

if(this.gd._context._scrollZoom.mapbox) {
map.scrollZoom.enable();
} else {
map.scrollZoom.disable();
}
};

proto.updateData = function(calcData) {
Expand Down
59 changes: 59 additions & 0 deletions test/jasmine/tests/config_test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -760,4 +760,63 @@ describe('config argument', function() {
.then(done);
});
});

describe('scrollZoom:', function() {
var gd;

beforeEach(function() { gd = createGraphDiv(); });

afterEach(destroyGraphDiv);

function plot(config) {
return Plotly.plot(gd, [], {}, config);
}

it('should fill in scrollZoom default', function(done) {
plot(undefined).then(function() {
expect(gd._context.scrollZoom).toBe('gl3d+geo+mapbox');
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, mapbox: 1});
Comment thread
archmoj marked this conversation as resolved.
expect(gd._context._scrollZoom.cartesian).toBe(undefined, 'no cartesian!');
})
.catch(failTest)
.then(done);
});

it('should fill in blank scrollZoom value', function(done) {
plot({scrollZoom: null}).then(function() {
expect(gd._context.scrollZoom).toBe(null);
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, mapbox: 1});
Comment thread
archmoj marked this conversation as resolved.
expect(gd._context._scrollZoom.cartesian).toBe(undefined, 'no cartesian!');
})
.catch(failTest)
.then(done);
});

it('should honor scrollZoom:true', function(done) {
plot({scrollZoom: true}).then(function() {
expect(gd._context.scrollZoom).toBe(true);
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, cartesian: 1, mapbox: 1});
})
.catch(failTest)
.then(done);
});

it('should honor scrollZoom:false', function(done) {
plot({scrollZoom: false}).then(function() {
expect(gd._context.scrollZoom).toBe(false);
expect(gd._context._scrollZoom).toEqual({});
})
.catch(failTest)
.then(done);
});

it('should honor scrollZoom flaglist', function(done) {
plot({scrollZoom: 'mapbox+cartesian'}).then(function() {
expect(gd._context.scrollZoom).toBe('mapbox+cartesian');
expect(gd._context._scrollZoom).toEqual({mapbox: 1, cartesian: 1});
})
.catch(failTest)
.then(done);
});
});
});
48 changes: 48 additions & 0 deletions test/jasmine/tests/geo_test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1961,4 +1961,52 @@ describe('Test geo zoom/pan/drag interactions:', function() {
.catch(failTest)
.then(done);
});

it('should respect scrollZoom config option', function(done) {
var fig = Lib.extendDeep({}, require('@mocks/geo_winkel-tripel'));
fig.layout.width = 700;
fig.layout.height = 500;
fig.layout.dragmode = 'pan';

function _assert(step, attr, proj, eventKeys) {
var msg = '[' + step + '] ';

var geoLayout = gd._fullLayout.geo;
var scale = geoLayout.projection.scale;
expect(scale).toBeCloseTo(attr[0], 1, msg + 'zoom');

var geo = geoLayout._subplot;
var _scale = geo.projection.scale();
expect(_scale).toBeCloseTo(proj[0], 0, msg + 'scale');

assertEventData(msg, eventKeys);
}

plot(fig)
.then(function() {
_assert('base', [1], [101.9], undefined);
})
.then(function() { return scroll([200, 250], [-200, -200]); })
.then(function() {
_assert('with scroll enable (by default)',
[1.3], [134.4],
['geo.projection.rotation.lon', 'geo.center.lon', 'geo.center.lat', 'geo.projection.scale']
);
})
.then(function() { return Plotly.plot(gd, [], {}, {scrollZoom: false}); })
.then(function() { return scroll([200, 250], [-200, -200]); })
.then(function() {
_assert('with scrollZoom:false', [1.3], [134.4], undefined);
})
.then(function() { return Plotly.plot(gd, [], {}, {scrollZoom: 'geo'}); })
.then(function() { return scroll([200, 250], [-200, -200]); })
.then(function() {
_assert('with scrollZoom:geo',
[1.74], [177.34],
['geo.projection.rotation.lon', 'geo.center.lon', 'geo.center.lat', 'geo.projection.scale']
);
})
.catch(failTest)
.then(done);
});
});
59 changes: 34 additions & 25 deletions test/jasmine/tests/gl3d_plot_interact_test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1097,6 +1097,11 @@ describe('Test gl3d drag and wheel interactions', function() {
}
};

function _assertAndReset(cnt) {
expect(relayoutCallback).toHaveBeenCalledTimes(cnt);
relayoutCallback.calls.reset();
}

Plotly.plot(gd, mock)
.then(function() {
relayoutCallback = jasmine.createSpy('relayoutCallback');
Expand All@@ -1115,48 +1120,32 @@ describe('Test gl3d drag and wheel interactions', function() {
return scroll(sceneTarget);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return scroll(sceneTarget2);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return drag(sceneTarget2, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return drag(sceneTarget, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

return Plotly.relayout(gd, {
'scene.dragmode': false,
'scene2.dragmode': false
});
_assertAndReset(1);
return Plotly.relayout(gd, {'scene.dragmode': false, 'scene2.dragmode': false});
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return drag(sceneTarget, [0, 0], [100, 100]);
})
.then(function() {
return drag(sceneTarget2, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(0);
_assertAndReset(0);

return Plotly.relayout(gd, {
'scene.dragmode': 'orbit',
'scene2.dragmode': 'turntable'
});
return Plotly.relayout(gd, {'scene.dragmode': 'orbit', 'scene2.dragmode': 'turntable'});
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
Expand All@@ -1168,7 +1157,27 @@ describe('Test gl3d drag and wheel interactions', function() {
return drag(sceneTarget2, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(2);
_assertAndReset(2);
return Plotly.plot(gd, [], {}, {scrollZoom: false});
})
.then(function() {
return scroll(sceneTarget);
})
.then(function() {
return scroll(sceneTarget2);
})
.then(function() {
_assertAndReset(0);
return Plotly.plot(gd, [], {}, {scrollZoom: 'gl3d'});
})
.then(function() {
return scroll(sceneTarget);
})
.then(function() {
return scroll(sceneTarget2);
})
.then(function() {
_assertAndReset(2);
})
.catch(failTest)
.then(done);
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/plot_api/plot_api.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -507,6 +507,25 @@ function setPlotContext(gd, config) {
// Check if gd has a specified widht/height to begin with
context._hasZeroHeight = context._hasZeroHeight || gd.clientHeight === 0;
context._hasZeroWidth = context._hasZeroWidth || gd.clientWidth === 0;

// fill context._scrollZoom helper to help manage scrollZoom flaglist
var szIn = context.scrollZoom;
var szOut = context._scrollZoom = {};
if(szIn === true) {
szOut.cartesian = 1;
szOut.gl3d = 1;
szOut.geo = 1;
szOut.mapbox = 1;
} else if(typeof szIn === 'string') {
var parts = szIn.split('+');
for(i = 0; i < parts.length; i++) {
szOut[parts[i]] = 1;
Comment thread
archmoj marked this conversation as resolved.
}
} else if(szIn !== false) {
szOut.gl3d = 1;
szOut.geo = 1;
szOut.mapbox = 1;
}
}

function plotLegacyPolar(gd, data, layout) {
Expand Down
13 changes: 9 additions & 4 deletions src/plot_api/plot_config.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -145,11 +145,16 @@ var configAttributes = {
},

scrollZoom: {
valType: 'boolean',
dflt: false,
valType: 'flaglist',
flags: ['cartesian', 'gl3d', 'geo', 'mapbox'],
extras: [true, false],
dflt: 'gl3d+geo+mapbox',
description: [
'Determines whether mouse wheel or two-finger scroll zooms is',
'enable. Has an effect only on cartesian subplots.'
'Determines whether mouse wheel or two-finger scroll zooms is enable.',
'Turned on by default for gl3d, geo and mapbox subplots',
'(as these subplot types do not have zoombox via pan),',
'but turned off by default for cartesian subplots.',
'Set `scrollZoom` to *false* to disable scrolling for all subplots.'
].join(' ')
},
doubleClick: {
Expand Down
2 changes: 1 addition & 1 deletion src/plots/cartesian/dragbox.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -417,7 +417,7 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
// deactivate mousewheel scrolling on embedded graphs
// devs can override this with layout._enablescrollzoom,
// but _ ensures this setting won't leave their page
if(!gd._context.scrollZoom && !gd._fullLayout._enablescrollzoom) {
if(!gd._context._scrollZoom.cartesian && !gd._fullLayout._enablescrollzoom) {
return;
}

Expand Down
3 changes: 3 additions & 0 deletions src/plots/geo/geo.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -420,6 +420,9 @@ proto.updateFx = function(fullLayout, geoLayout) {
bgRect.node().onmousedown = null;
bgRect.call(createGeoZoom(_this, geoLayout));
bgRect.on('dblclick.zoom', zoomReset);
if(!gd._context._scrollZoom.geo) {
bgRect.on('wheel.zoom', null);
}
}
else if(dragMode === 'select' || dragMode === 'lasso') {
bgRect.on('.zoom', null);
Expand Down
2 changes: 1 addition & 1 deletion src/plots/gl2d/scene2d.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,7 +38,7 @@ function Scene2D(options, fullLayout) {
this.pixelRatio = options.plotGlPixelRatio || window.devicePixelRatio;
this.id = options.id;
this.staticPlot = !!options.staticPlot;
this.scrollZoom = this.graphDiv._context.scrollZoom;
this.scrollZoom = this.graphDiv._context._scrollZoom.cartesian;

this.fullData = null;
this.updateRefs(fullLayout);
Expand Down
3 changes: 3 additions & 0 deletions src/plots/gl3d/camera.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,6 +48,7 @@ function createCamera(element, options) {

var camera = {
keyBindingMode: 'rotate',
enableWheel: true,
Comment thread
archmoj marked this conversation as resolved.
view: view,
element: element,
delay: options.delay || 16,
Expand DownExpand Up@@ -257,7 +258,9 @@ function createCamera(element, options) {
}

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

var flipX = camera.flipX ? 1 : -1;
var flipY = camera.flipY ? 1 : -1;
Expand Down
14 changes: 10 additions & 4 deletions src/plots/gl3d/scene.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -227,8 +227,15 @@ function initializeGLPlot(scene, canvas, gl) {
scene.graphDiv.emit('plotly_relayout', update);
};

scene.glplot.canvas.addEventListener('mouseup', relayoutCallback.bind(null, scene));
scene.glplot.canvas.addEventListener('wheel', relayoutCallback.bind(null, scene), passiveSupported ? {passive: false} : false);
scene.glplot.canvas.addEventListener('mouseup', function() {
relayoutCallback(scene);
});

scene.glplot.canvas.addEventListener('wheel', function() {
if(gd._context._scrollZoom.gl3d) {
relayoutCallback(scene);
}
}, passiveSupported ? {passive: false} : false);
Comment on lines +234 to +238

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've disabled scrollZoom in the plot config, but the mouse is still getting captured when scrolling by the canvas. Looks like it's because of the wheel event listener that's being registered here anyway. After I remove the wheel listener on scene element from chrome inspector, scrolling the page no longer gets captured by the canvas.

@etpinard, can we not add the listener at all if scrollzoom is disabled?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zfei thanks for the debugging. This comment is going to get lost here on a merged PR though, can you make a new issue for this? Or better yet a PR, looks like it's probably a pretty easy fix 😎

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @alexcjohnson.

I've never touched the Plotly codebase and haven't set up my mind to go through the learning curve yet. 😉

Will just leave it as an issue for now.


if(!scene.staticMode) {
scene.glplot.canvas.addEventListener('webglcontextlost', function(event) {
Expand DownExpand Up@@ -385,7 +392,6 @@ function computeTraceBounds(scene, trace, bounds) {
}

proto.plot = function(sceneData, fullLayout, layout) {

// Save parameters
this.plotArgs = [sceneData, fullLayout, layout];

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

// Update scene
this.glplot.update({});
Expand DownExpand Up@@ -776,7 +783,6 @@ proto.updateFx = function(dragmode, hovermode) {
fullCamera.up = zUp;
Lib.nestedProperty(layout, attr).set(zUp);
} else {

// none rotation modes [pan or zoom]
camera.keyBindingMode = dragmode;
}
Expand Down
6 changes: 6 additions & 0 deletions src/plots/mapbox/mapbox.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -258,6 +258,12 @@ proto.updateMap = function(calcData, fullLayout, resolve, reject) {
self.updateLayout(fullLayout);
self.resolveOnRender(resolve);
}

if(this.gd._context._scrollZoom.mapbox) {
map.scrollZoom.enable();
} else {
map.scrollZoom.disable();
}
};

proto.updateData = function(calcData) {
Expand Down
59 changes: 59 additions & 0 deletions test/jasmine/tests/config_test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -760,4 +760,63 @@ describe('config argument', function() {
.then(done);
});
});

describe('scrollZoom:', function() {
var gd;

beforeEach(function() { gd = createGraphDiv(); });

afterEach(destroyGraphDiv);

function plot(config) {
return Plotly.plot(gd, [], {}, config);
}

it('should fill in scrollZoom default', function(done) {
plot(undefined).then(function() {
expect(gd._context.scrollZoom).toBe('gl3d+geo+mapbox');
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, mapbox: 1});
Comment thread
archmoj marked this conversation as resolved.
expect(gd._context._scrollZoom.cartesian).toBe(undefined, 'no cartesian!');
})
.catch(failTest)
.then(done);
});

it('should fill in blank scrollZoom value', function(done) {
plot({scrollZoom: null}).then(function() {
expect(gd._context.scrollZoom).toBe(null);
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, mapbox: 1});
Comment thread
archmoj marked this conversation as resolved.
expect(gd._context._scrollZoom.cartesian).toBe(undefined, 'no cartesian!');
})
.catch(failTest)
.then(done);
});

it('should honor scrollZoom:true', function(done) {
plot({scrollZoom: true}).then(function() {
expect(gd._context.scrollZoom).toBe(true);
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, cartesian: 1, mapbox: 1});
})
.catch(failTest)
.then(done);
});

it('should honor scrollZoom:false', function(done) {
plot({scrollZoom: false}).then(function() {
expect(gd._context.scrollZoom).toBe(false);
expect(gd._context._scrollZoom).toEqual({});
})
.catch(failTest)
.then(done);
});

it('should honor scrollZoom flaglist', function(done) {
plot({scrollZoom: 'mapbox+cartesian'}).then(function() {
expect(gd._context.scrollZoom).toBe('mapbox+cartesian');
expect(gd._context._scrollZoom).toEqual({mapbox: 1, cartesian: 1});
})
.catch(failTest)
.then(done);
});
});
});
48 changes: 48 additions & 0 deletions test/jasmine/tests/geo_test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1961,4 +1961,52 @@ describe('Test geo zoom/pan/drag interactions:', function() {
.catch(failTest)
.then(done);
});

it('should respect scrollZoom config option', function(done) {
var fig = Lib.extendDeep({}, require('@mocks/geo_winkel-tripel'));
fig.layout.width = 700;
fig.layout.height = 500;
fig.layout.dragmode = 'pan';

function _assert(step, attr, proj, eventKeys) {
var msg = '[' + step + '] ';

var geoLayout = gd._fullLayout.geo;
var scale = geoLayout.projection.scale;
expect(scale).toBeCloseTo(attr[0], 1, msg + 'zoom');

var geo = geoLayout._subplot;
var _scale = geo.projection.scale();
expect(_scale).toBeCloseTo(proj[0], 0, msg + 'scale');

assertEventData(msg, eventKeys);
}

plot(fig)
.then(function() {
_assert('base', [1], [101.9], undefined);
})
.then(function() { return scroll([200, 250], [-200, -200]); })
.then(function() {
_assert('with scroll enable (by default)',
[1.3], [134.4],
['geo.projection.rotation.lon', 'geo.center.lon', 'geo.center.lat', 'geo.projection.scale']
);
})
.then(function() { return Plotly.plot(gd, [], {}, {scrollZoom: false}); })
.then(function() { return scroll([200, 250], [-200, -200]); })
.then(function() {
_assert('with scrollZoom:false', [1.3], [134.4], undefined);
})
.then(function() { return Plotly.plot(gd, [], {}, {scrollZoom: 'geo'}); })
.then(function() { return scroll([200, 250], [-200, -200]); })
.then(function() {
_assert('with scrollZoom:geo',
[1.74], [177.34],
['geo.projection.rotation.lon', 'geo.center.lon', 'geo.center.lat', 'geo.projection.scale']
);
})
.catch(failTest)
.then(done);
});
});
59 changes: 34 additions & 25 deletions test/jasmine/tests/gl3d_plot_interact_test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1097,6 +1097,11 @@ describe('Test gl3d drag and wheel interactions', function() {
}
};

function _assertAndReset(cnt) {
expect(relayoutCallback).toHaveBeenCalledTimes(cnt);
relayoutCallback.calls.reset();
}

Plotly.plot(gd, mock)
.then(function() {
relayoutCallback = jasmine.createSpy('relayoutCallback');
Expand All@@ -1115,48 +1120,32 @@ describe('Test gl3d drag and wheel interactions', function() {
return scroll(sceneTarget);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return scroll(sceneTarget2);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return drag(sceneTarget2, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return drag(sceneTarget, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

return Plotly.relayout(gd, {
'scene.dragmode': false,
'scene2.dragmode': false
});
_assertAndReset(1);
return Plotly.relayout(gd, {'scene.dragmode': false, 'scene2.dragmode': false});
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return drag(sceneTarget, [0, 0], [100, 100]);
})
.then(function() {
return drag(sceneTarget2, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(0);
_assertAndReset(0);

return Plotly.relayout(gd, {
'scene.dragmode': 'orbit',
'scene2.dragmode': 'turntable'
});
return Plotly.relayout(gd, {'scene.dragmode': 'orbit', 'scene2.dragmode': 'turntable'});
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
Expand All@@ -1168,7 +1157,27 @@ describe('Test gl3d drag and wheel interactions', function() {
return drag(sceneTarget2, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(2);
_assertAndReset(2);
return Plotly.plot(gd, [], {}, {scrollZoom: false});
})
.then(function() {
return scroll(sceneTarget);
})
.then(function() {
return scroll(sceneTarget2);
})
.then(function() {
_assertAndReset(0);
return Plotly.plot(gd, [], {}, {scrollZoom: 'gl3d'});
})
.then(function() {
return scroll(sceneTarget);
})
.then(function() {
return scroll(sceneTarget2);
})
.then(function() {
_assertAndReset(2);
})
.catch(failTest)
.then(done);
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/plot_api/plot_api.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -507,6 +507,25 @@ function setPlotContext(gd, config) {
// Check if gd has a specified widht/height to begin with
context._hasZeroHeight = context._hasZeroHeight || gd.clientHeight === 0;
context._hasZeroWidth = context._hasZeroWidth || gd.clientWidth === 0;

// fill context._scrollZoom helper to help manage scrollZoom flaglist
var szIn = context.scrollZoom;
var szOut = context._scrollZoom = {};
if(szIn === true) {
szOut.cartesian = 1;
szOut.gl3d = 1;
szOut.geo = 1;
szOut.mapbox = 1;
} else if(typeof szIn === 'string') {
var parts = szIn.split('+');
for(i = 0; i < parts.length; i++) {
szOut[parts[i]] = 1;
Comment thread
archmoj marked this conversation as resolved.
}
} else if(szIn !== false) {
szOut.gl3d = 1;
szOut.geo = 1;
szOut.mapbox = 1;
}
}

function plotLegacyPolar(gd, data, layout) {
Expand Down
13 changes: 9 additions & 4 deletions src/plot_api/plot_config.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -145,11 +145,16 @@ var configAttributes = {
},

scrollZoom: {
valType: 'boolean',
dflt: false,
valType: 'flaglist',
flags: ['cartesian', 'gl3d', 'geo', 'mapbox'],
extras: [true, false],
dflt: 'gl3d+geo+mapbox',
description: [
'Determines whether mouse wheel or two-finger scroll zooms is',
'enable. Has an effect only on cartesian subplots.'
'Determines whether mouse wheel or two-finger scroll zooms is enable.',
'Turned on by default for gl3d, geo and mapbox subplots',
'(as these subplot types do not have zoombox via pan),',
'but turned off by default for cartesian subplots.',
'Set `scrollZoom` to *false* to disable scrolling for all subplots.'
].join(' ')
},
doubleClick: {
Expand Down
2 changes: 1 addition & 1 deletion src/plots/cartesian/dragbox.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -417,7 +417,7 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
// deactivate mousewheel scrolling on embedded graphs
// devs can override this with layout._enablescrollzoom,
// but _ ensures this setting won't leave their page
if(!gd._context.scrollZoom && !gd._fullLayout._enablescrollzoom) {
if(!gd._context._scrollZoom.cartesian && !gd._fullLayout._enablescrollzoom) {
return;
}

Expand Down
3 changes: 3 additions & 0 deletions src/plots/geo/geo.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -420,6 +420,9 @@ proto.updateFx = function(fullLayout, geoLayout) {
bgRect.node().onmousedown = null;
bgRect.call(createGeoZoom(_this, geoLayout));
bgRect.on('dblclick.zoom', zoomReset);
if(!gd._context._scrollZoom.geo) {
bgRect.on('wheel.zoom', null);
}
}
else if(dragMode === 'select' || dragMode === 'lasso') {
bgRect.on('.zoom', null);
Expand Down
2 changes: 1 addition & 1 deletion src/plots/gl2d/scene2d.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,7 +38,7 @@ function Scene2D(options, fullLayout) {
this.pixelRatio = options.plotGlPixelRatio || window.devicePixelRatio;
this.id = options.id;
this.staticPlot = !!options.staticPlot;
this.scrollZoom = this.graphDiv._context.scrollZoom;
this.scrollZoom = this.graphDiv._context._scrollZoom.cartesian;

this.fullData = null;
this.updateRefs(fullLayout);
Expand Down
3 changes: 3 additions & 0 deletions src/plots/gl3d/camera.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,6 +48,7 @@ function createCamera(element, options) {

var camera = {
keyBindingMode: 'rotate',
enableWheel: true,
Comment thread
archmoj marked this conversation as resolved.
view: view,
element: element,
delay: options.delay || 16,
Expand DownExpand Up@@ -257,7 +258,9 @@ function createCamera(element, options) {
}

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

var flipX = camera.flipX ? 1 : -1;
var flipY = camera.flipY ? 1 : -1;
Expand Down
14 changes: 10 additions & 4 deletions src/plots/gl3d/scene.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -227,8 +227,15 @@ function initializeGLPlot(scene, canvas, gl) {
scene.graphDiv.emit('plotly_relayout', update);
};

scene.glplot.canvas.addEventListener('mouseup', relayoutCallback.bind(null, scene));
scene.glplot.canvas.addEventListener('wheel', relayoutCallback.bind(null, scene), passiveSupported ? {passive: false} : false);
scene.glplot.canvas.addEventListener('mouseup', function() {
relayoutCallback(scene);
});

scene.glplot.canvas.addEventListener('wheel', function() {
if(gd._context._scrollZoom.gl3d) {
relayoutCallback(scene);
}
}, passiveSupported ? {passive: false} : false);
Comment on lines +234 to +238

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've disabled scrollZoom in the plot config, but the mouse is still getting captured when scrolling by the canvas. Looks like it's because of the wheel event listener that's being registered here anyway. After I remove the wheel listener on scene element from chrome inspector, scrolling the page no longer gets captured by the canvas.

@etpinard, can we not add the listener at all if scrollzoom is disabled?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zfei thanks for the debugging. This comment is going to get lost here on a merged PR though, can you make a new issue for this? Or better yet a PR, looks like it's probably a pretty easy fix 😎

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @alexcjohnson.

I've never touched the Plotly codebase and haven't set up my mind to go through the learning curve yet. 😉

Will just leave it as an issue for now.


if(!scene.staticMode) {
scene.glplot.canvas.addEventListener('webglcontextlost', function(event) {
Expand DownExpand Up@@ -385,7 +392,6 @@ function computeTraceBounds(scene, trace, bounds) {
}

proto.plot = function(sceneData, fullLayout, layout) {

// Save parameters
this.plotArgs = [sceneData, fullLayout, layout];

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

// Update scene
this.glplot.update({});
Expand DownExpand Up@@ -776,7 +783,6 @@ proto.updateFx = function(dragmode, hovermode) {
fullCamera.up = zUp;
Lib.nestedProperty(layout, attr).set(zUp);
} else {

// none rotation modes [pan or zoom]
camera.keyBindingMode = dragmode;
}
Expand Down
6 changes: 6 additions & 0 deletions src/plots/mapbox/mapbox.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -258,6 +258,12 @@ proto.updateMap = function(calcData, fullLayout, resolve, reject) {
self.updateLayout(fullLayout);
self.resolveOnRender(resolve);
}

if(this.gd._context._scrollZoom.mapbox) {
map.scrollZoom.enable();
} else {
map.scrollZoom.disable();
}
};

proto.updateData = function(calcData) {
Expand Down
59 changes: 59 additions & 0 deletions test/jasmine/tests/config_test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -760,4 +760,63 @@ describe('config argument', function() {
.then(done);
});
});

describe('scrollZoom:', function() {
var gd;

beforeEach(function() { gd = createGraphDiv(); });

afterEach(destroyGraphDiv);

function plot(config) {
return Plotly.plot(gd, [], {}, config);
}

it('should fill in scrollZoom default', function(done) {
plot(undefined).then(function() {
expect(gd._context.scrollZoom).toBe('gl3d+geo+mapbox');
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, mapbox: 1});
Comment thread
archmoj marked this conversation as resolved.
expect(gd._context._scrollZoom.cartesian).toBe(undefined, 'no cartesian!');
})
.catch(failTest)
.then(done);
});

it('should fill in blank scrollZoom value', function(done) {
plot({scrollZoom: null}).then(function() {
expect(gd._context.scrollZoom).toBe(null);
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, mapbox: 1});
Comment thread
archmoj marked this conversation as resolved.
expect(gd._context._scrollZoom.cartesian).toBe(undefined, 'no cartesian!');
})
.catch(failTest)
.then(done);
});

it('should honor scrollZoom:true', function(done) {
plot({scrollZoom: true}).then(function() {
expect(gd._context.scrollZoom).toBe(true);
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, cartesian: 1, mapbox: 1});
})
.catch(failTest)
.then(done);
});

it('should honor scrollZoom:false', function(done) {
plot({scrollZoom: false}).then(function() {
expect(gd._context.scrollZoom).toBe(false);
expect(gd._context._scrollZoom).toEqual({});
})
.catch(failTest)
.then(done);
});

it('should honor scrollZoom flaglist', function(done) {
plot({scrollZoom: 'mapbox+cartesian'}).then(function() {
expect(gd._context.scrollZoom).toBe('mapbox+cartesian');
expect(gd._context._scrollZoom).toEqual({mapbox: 1, cartesian: 1});
})
.catch(failTest)
.then(done);
});
});
});
48 changes: 48 additions & 0 deletions test/jasmine/tests/geo_test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1961,4 +1961,52 @@ describe('Test geo zoom/pan/drag interactions:', function() {
.catch(failTest)
.then(done);
});

it('should respect scrollZoom config option', function(done) {
var fig = Lib.extendDeep({}, require('@mocks/geo_winkel-tripel'));
fig.layout.width = 700;
fig.layout.height = 500;
fig.layout.dragmode = 'pan';

function _assert(step, attr, proj, eventKeys) {
var msg = '[' + step + '] ';

var geoLayout = gd._fullLayout.geo;
var scale = geoLayout.projection.scale;
expect(scale).toBeCloseTo(attr[0], 1, msg + 'zoom');

var geo = geoLayout._subplot;
var _scale = geo.projection.scale();
expect(_scale).toBeCloseTo(proj[0], 0, msg + 'scale');

assertEventData(msg, eventKeys);
}

plot(fig)
.then(function() {
_assert('base', [1], [101.9], undefined);
})
.then(function() { return scroll([200, 250], [-200, -200]); })
.then(function() {
_assert('with scroll enable (by default)',
[1.3], [134.4],
['geo.projection.rotation.lon', 'geo.center.lon', 'geo.center.lat', 'geo.projection.scale']
);
})
.then(function() { return Plotly.plot(gd, [], {}, {scrollZoom: false}); })
.then(function() { return scroll([200, 250], [-200, -200]); })
.then(function() {
_assert('with scrollZoom:false', [1.3], [134.4], undefined);
})
.then(function() { return Plotly.plot(gd, [], {}, {scrollZoom: 'geo'}); })
.then(function() { return scroll([200, 250], [-200, -200]); })
.then(function() {
_assert('with scrollZoom:geo',
[1.74], [177.34],
['geo.projection.rotation.lon', 'geo.center.lon', 'geo.center.lat', 'geo.projection.scale']
);
})
.catch(failTest)
.then(done);
});
});
59 changes: 34 additions & 25 deletions test/jasmine/tests/gl3d_plot_interact_test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1097,6 +1097,11 @@ describe('Test gl3d drag and wheel interactions', function() {
}
};

function _assertAndReset(cnt) {
expect(relayoutCallback).toHaveBeenCalledTimes(cnt);
relayoutCallback.calls.reset();
}

Plotly.plot(gd, mock)
.then(function() {
relayoutCallback = jasmine.createSpy('relayoutCallback');
Expand All@@ -1115,48 +1120,32 @@ describe('Test gl3d drag and wheel interactions', function() {
return scroll(sceneTarget);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return scroll(sceneTarget2);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return drag(sceneTarget2, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return drag(sceneTarget, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

return Plotly.relayout(gd, {
'scene.dragmode': false,
'scene2.dragmode': false
});
_assertAndReset(1);
return Plotly.relayout(gd, {'scene.dragmode': false, 'scene2.dragmode': false});
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return drag(sceneTarget, [0, 0], [100, 100]);
})
.then(function() {
return drag(sceneTarget2, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(0);
_assertAndReset(0);

return Plotly.relayout(gd, {
'scene.dragmode': 'orbit',
'scene2.dragmode': 'turntable'
});
return Plotly.relayout(gd, {'scene.dragmode': 'orbit', 'scene2.dragmode': 'turntable'});
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
Expand All@@ -1168,7 +1157,27 @@ describe('Test gl3d drag and wheel interactions', function() {
return drag(sceneTarget2, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(2);
_assertAndReset(2);
return Plotly.plot(gd, [], {}, {scrollZoom: false});
})
.then(function() {
return scroll(sceneTarget);
})
.then(function() {
return scroll(sceneTarget2);
})
.then(function() {
_assertAndReset(0);
return Plotly.plot(gd, [], {}, {scrollZoom: 'gl3d'});
})
.then(function() {
return scroll(sceneTarget);
})
.then(function() {
return scroll(sceneTarget2);
})
.then(function() {
_assertAndReset(2);
})
.catch(failTest)
.then(done);
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/plot_api/plot_api.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -507,6 +507,25 @@ function setPlotContext(gd, config) {
// Check if gd has a specified widht/height to begin with
context._hasZeroHeight = context._hasZeroHeight || gd.clientHeight === 0;
context._hasZeroWidth = context._hasZeroWidth || gd.clientWidth === 0;

// fill context._scrollZoom helper to help manage scrollZoom flaglist
var szIn = context.scrollZoom;
var szOut = context._scrollZoom = {};
if(szIn === true) {
szOut.cartesian = 1;
szOut.gl3d = 1;
szOut.geo = 1;
szOut.mapbox = 1;
} else if(typeof szIn === 'string') {
var parts = szIn.split('+');
for(i = 0; i < parts.length; i++) {
szOut[parts[i]] = 1;
Comment thread
archmoj marked this conversation as resolved.
}
} else if(szIn !== false) {
szOut.gl3d = 1;
szOut.geo = 1;
szOut.mapbox = 1;
}
}

function plotLegacyPolar(gd, data, layout) {
Expand Down
13 changes: 9 additions & 4 deletions src/plot_api/plot_config.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -145,11 +145,16 @@ var configAttributes = {
},

scrollZoom: {
valType: 'boolean',
dflt: false,
valType: 'flaglist',
flags: ['cartesian', 'gl3d', 'geo', 'mapbox'],
extras: [true, false],
dflt: 'gl3d+geo+mapbox',
description: [
'Determines whether mouse wheel or two-finger scroll zooms is',
'enable. Has an effect only on cartesian subplots.'
'Determines whether mouse wheel or two-finger scroll zooms is enable.',
'Turned on by default for gl3d, geo and mapbox subplots',
'(as these subplot types do not have zoombox via pan),',
'but turned off by default for cartesian subplots.',
'Set `scrollZoom` to *false* to disable scrolling for all subplots.'
].join(' ')
},
doubleClick: {
Expand Down
2 changes: 1 addition & 1 deletion src/plots/cartesian/dragbox.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -417,7 +417,7 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
// deactivate mousewheel scrolling on embedded graphs
// devs can override this with layout._enablescrollzoom,
// but _ ensures this setting won't leave their page
if(!gd._context.scrollZoom && !gd._fullLayout._enablescrollzoom) {
if(!gd._context._scrollZoom.cartesian && !gd._fullLayout._enablescrollzoom) {
return;
}

Expand Down
3 changes: 3 additions & 0 deletions src/plots/geo/geo.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -420,6 +420,9 @@ proto.updateFx = function(fullLayout, geoLayout) {
bgRect.node().onmousedown = null;
bgRect.call(createGeoZoom(_this, geoLayout));
bgRect.on('dblclick.zoom', zoomReset);
if(!gd._context._scrollZoom.geo) {
bgRect.on('wheel.zoom', null);
}
}
else if(dragMode === 'select' || dragMode === 'lasso') {
bgRect.on('.zoom', null);
Expand Down
2 changes: 1 addition & 1 deletion src/plots/gl2d/scene2d.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,7 +38,7 @@ function Scene2D(options, fullLayout) {
this.pixelRatio = options.plotGlPixelRatio || window.devicePixelRatio;
this.id = options.id;
this.staticPlot = !!options.staticPlot;
this.scrollZoom = this.graphDiv._context.scrollZoom;
this.scrollZoom = this.graphDiv._context._scrollZoom.cartesian;

this.fullData = null;
this.updateRefs(fullLayout);
Expand Down
3 changes: 3 additions & 0 deletions src/plots/gl3d/camera.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,6 +48,7 @@ function createCamera(element, options) {

var camera = {
keyBindingMode: 'rotate',
enableWheel: true,
Comment thread
archmoj marked this conversation as resolved.
view: view,
element: element,
delay: options.delay || 16,
Expand DownExpand Up@@ -257,7 +258,9 @@ function createCamera(element, options) {
}

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

var flipX = camera.flipX ? 1 : -1;
var flipY = camera.flipY ? 1 : -1;
Expand Down
14 changes: 10 additions & 4 deletions src/plots/gl3d/scene.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -227,8 +227,15 @@ function initializeGLPlot(scene, canvas, gl) {
scene.graphDiv.emit('plotly_relayout', update);
};

scene.glplot.canvas.addEventListener('mouseup', relayoutCallback.bind(null, scene));
scene.glplot.canvas.addEventListener('wheel', relayoutCallback.bind(null, scene), passiveSupported ? {passive: false} : false);
scene.glplot.canvas.addEventListener('mouseup', function() {
relayoutCallback(scene);
});

scene.glplot.canvas.addEventListener('wheel', function() {
if(gd._context._scrollZoom.gl3d) {
relayoutCallback(scene);
}
}, passiveSupported ? {passive: false} : false);
Comment on lines +234 to +238

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've disabled scrollZoom in the plot config, but the mouse is still getting captured when scrolling by the canvas. Looks like it's because of the wheel event listener that's being registered here anyway. After I remove the wheel listener on scene element from chrome inspector, scrolling the page no longer gets captured by the canvas.

@etpinard, can we not add the listener at all if scrollzoom is disabled?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zfei thanks for the debugging. This comment is going to get lost here on a merged PR though, can you make a new issue for this? Or better yet a PR, looks like it's probably a pretty easy fix 😎

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @alexcjohnson.

I've never touched the Plotly codebase and haven't set up my mind to go through the learning curve yet. 😉

Will just leave it as an issue for now.


if(!scene.staticMode) {
scene.glplot.canvas.addEventListener('webglcontextlost', function(event) {
Expand DownExpand Up@@ -385,7 +392,6 @@ function computeTraceBounds(scene, trace, bounds) {
}

proto.plot = function(sceneData, fullLayout, layout) {

// Save parameters
this.plotArgs = [sceneData, fullLayout, layout];

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

// Update scene
this.glplot.update({});
Expand DownExpand Up@@ -776,7 +783,6 @@ proto.updateFx = function(dragmode, hovermode) {
fullCamera.up = zUp;
Lib.nestedProperty(layout, attr).set(zUp);
} else {

// none rotation modes [pan or zoom]
camera.keyBindingMode = dragmode;
}
Expand Down
6 changes: 6 additions & 0 deletions src/plots/mapbox/mapbox.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -258,6 +258,12 @@ proto.updateMap = function(calcData, fullLayout, resolve, reject) {
self.updateLayout(fullLayout);
self.resolveOnRender(resolve);
}

if(this.gd._context._scrollZoom.mapbox) {
map.scrollZoom.enable();
} else {
map.scrollZoom.disable();
}
};

proto.updateData = function(calcData) {
Expand Down
59 changes: 59 additions & 0 deletions test/jasmine/tests/config_test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -760,4 +760,63 @@ describe('config argument', function() {
.then(done);
});
});

describe('scrollZoom:', function() {
var gd;

beforeEach(function() { gd = createGraphDiv(); });

afterEach(destroyGraphDiv);

function plot(config) {
return Plotly.plot(gd, [], {}, config);
}

it('should fill in scrollZoom default', function(done) {
plot(undefined).then(function() {
expect(gd._context.scrollZoom).toBe('gl3d+geo+mapbox');
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, mapbox: 1});
Comment thread
archmoj marked this conversation as resolved.
expect(gd._context._scrollZoom.cartesian).toBe(undefined, 'no cartesian!');
})
.catch(failTest)
.then(done);
});

it('should fill in blank scrollZoom value', function(done) {
plot({scrollZoom: null}).then(function() {
expect(gd._context.scrollZoom).toBe(null);
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, mapbox: 1});
Comment thread
archmoj marked this conversation as resolved.
expect(gd._context._scrollZoom.cartesian).toBe(undefined, 'no cartesian!');
})
.catch(failTest)
.then(done);
});

it('should honor scrollZoom:true', function(done) {
plot({scrollZoom: true}).then(function() {
expect(gd._context.scrollZoom).toBe(true);
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, cartesian: 1, mapbox: 1});
})
.catch(failTest)
.then(done);
});

it('should honor scrollZoom:false', function(done) {
plot({scrollZoom: false}).then(function() {
expect(gd._context.scrollZoom).toBe(false);
expect(gd._context._scrollZoom).toEqual({});
})
.catch(failTest)
.then(done);
});

it('should honor scrollZoom flaglist', function(done) {
plot({scrollZoom: 'mapbox+cartesian'}).then(function() {
expect(gd._context.scrollZoom).toBe('mapbox+cartesian');
expect(gd._context._scrollZoom).toEqual({mapbox: 1, cartesian: 1});
})
.catch(failTest)
.then(done);
});
});
});
48 changes: 48 additions & 0 deletions test/jasmine/tests/geo_test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1961,4 +1961,52 @@ describe('Test geo zoom/pan/drag interactions:', function() {
.catch(failTest)
.then(done);
});

it('should respect scrollZoom config option', function(done) {
var fig = Lib.extendDeep({}, require('@mocks/geo_winkel-tripel'));
fig.layout.width = 700;
fig.layout.height = 500;
fig.layout.dragmode = 'pan';

function _assert(step, attr, proj, eventKeys) {
var msg = '[' + step + '] ';

var geoLayout = gd._fullLayout.geo;
var scale = geoLayout.projection.scale;
expect(scale).toBeCloseTo(attr[0], 1, msg + 'zoom');

var geo = geoLayout._subplot;
var _scale = geo.projection.scale();
expect(_scale).toBeCloseTo(proj[0], 0, msg + 'scale');

assertEventData(msg, eventKeys);
}

plot(fig)
.then(function() {
_assert('base', [1], [101.9], undefined);
})
.then(function() { return scroll([200, 250], [-200, -200]); })
.then(function() {
_assert('with scroll enable (by default)',
[1.3], [134.4],
['geo.projection.rotation.lon', 'geo.center.lon', 'geo.center.lat', 'geo.projection.scale']
);
})
.then(function() { return Plotly.plot(gd, [], {}, {scrollZoom: false}); })
.then(function() { return scroll([200, 250], [-200, -200]); })
.then(function() {
_assert('with scrollZoom:false', [1.3], [134.4], undefined);
})
.then(function() { return Plotly.plot(gd, [], {}, {scrollZoom: 'geo'}); })
.then(function() { return scroll([200, 250], [-200, -200]); })
.then(function() {
_assert('with scrollZoom:geo',
[1.74], [177.34],
['geo.projection.rotation.lon', 'geo.center.lon', 'geo.center.lat', 'geo.projection.scale']
);
})
.catch(failTest)
.then(done);
});
});
59 changes: 34 additions & 25 deletions test/jasmine/tests/gl3d_plot_interact_test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1097,6 +1097,11 @@ describe('Test gl3d drag and wheel interactions', function() {
}
};

function _assertAndReset(cnt) {
expect(relayoutCallback).toHaveBeenCalledTimes(cnt);
relayoutCallback.calls.reset();
}

Plotly.plot(gd, mock)
.then(function() {
relayoutCallback = jasmine.createSpy('relayoutCallback');
Expand All@@ -1115,48 +1120,32 @@ describe('Test gl3d drag and wheel interactions', function() {
return scroll(sceneTarget);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return scroll(sceneTarget2);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return drag(sceneTarget2, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return drag(sceneTarget, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

return Plotly.relayout(gd, {
'scene.dragmode': false,
'scene2.dragmode': false
});
_assertAndReset(1);
return Plotly.relayout(gd, {'scene.dragmode': false, 'scene2.dragmode': false});
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return drag(sceneTarget, [0, 0], [100, 100]);
})
.then(function() {
return drag(sceneTarget2, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(0);
_assertAndReset(0);

return Plotly.relayout(gd, {
'scene.dragmode': 'orbit',
'scene2.dragmode': 'turntable'
});
return Plotly.relayout(gd, {'scene.dragmode': 'orbit', 'scene2.dragmode': 'turntable'});
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
Expand All@@ -1168,7 +1157,27 @@ describe('Test gl3d drag and wheel interactions', function() {
return drag(sceneTarget2, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(2);
_assertAndReset(2);
return Plotly.plot(gd, [], {}, {scrollZoom: false});
})
.then(function() {
return scroll(sceneTarget);
})
.then(function() {
return scroll(sceneTarget2);
})
.then(function() {
_assertAndReset(0);
return Plotly.plot(gd, [], {}, {scrollZoom: 'gl3d'});
})
.then(function() {
return scroll(sceneTarget);
})
.then(function() {
return scroll(sceneTarget2);
})
.then(function() {
_assertAndReset(2);
})
.catch(failTest)
.then(done);
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/plot_api/plot_api.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -507,6 +507,25 @@ function setPlotContext(gd, config) {
// Check if gd has a specified widht/height to begin with
context._hasZeroHeight = context._hasZeroHeight || gd.clientHeight === 0;
context._hasZeroWidth = context._hasZeroWidth || gd.clientWidth === 0;

// fill context._scrollZoom helper to help manage scrollZoom flaglist
var szIn = context.scrollZoom;
var szOut = context._scrollZoom = {};
if(szIn === true) {
szOut.cartesian = 1;
szOut.gl3d = 1;
szOut.geo = 1;
szOut.mapbox = 1;
} else if(typeof szIn === 'string') {
var parts = szIn.split('+');
for(i = 0; i < parts.length; i++) {
szOut[parts[i]] = 1;
Comment thread
archmoj marked this conversation as resolved.
}
} else if(szIn !== false) {
szOut.gl3d = 1;
szOut.geo = 1;
szOut.mapbox = 1;
}
}

function plotLegacyPolar(gd, data, layout) {
Expand Down
13 changes: 9 additions & 4 deletions src/plot_api/plot_config.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -145,11 +145,16 @@ var configAttributes = {
},

scrollZoom: {
valType: 'boolean',
dflt: false,
valType: 'flaglist',
flags: ['cartesian', 'gl3d', 'geo', 'mapbox'],
extras: [true, false],
dflt: 'gl3d+geo+mapbox',
description: [
'Determines whether mouse wheel or two-finger scroll zooms is',
'enable. Has an effect only on cartesian subplots.'
'Determines whether mouse wheel or two-finger scroll zooms is enable.',
'Turned on by default for gl3d, geo and mapbox subplots',
'(as these subplot types do not have zoombox via pan),',
'but turned off by default for cartesian subplots.',
'Set `scrollZoom` to *false* to disable scrolling for all subplots.'
].join(' ')
},
doubleClick: {
Expand Down
2 changes: 1 addition & 1 deletion src/plots/cartesian/dragbox.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -417,7 +417,7 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
// deactivate mousewheel scrolling on embedded graphs
// devs can override this with layout._enablescrollzoom,
// but _ ensures this setting won't leave their page
if(!gd._context.scrollZoom && !gd._fullLayout._enablescrollzoom) {
if(!gd._context._scrollZoom.cartesian && !gd._fullLayout._enablescrollzoom) {
return;
}

Expand Down
3 changes: 3 additions & 0 deletions src/plots/geo/geo.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -420,6 +420,9 @@ proto.updateFx = function(fullLayout, geoLayout) {
bgRect.node().onmousedown = null;
bgRect.call(createGeoZoom(_this, geoLayout));
bgRect.on('dblclick.zoom', zoomReset);
if(!gd._context._scrollZoom.geo) {
bgRect.on('wheel.zoom', null);
}
}
else if(dragMode === 'select' || dragMode === 'lasso') {
bgRect.on('.zoom', null);
Expand Down
2 changes: 1 addition & 1 deletion src/plots/gl2d/scene2d.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,7 +38,7 @@ function Scene2D(options, fullLayout) {
this.pixelRatio = options.plotGlPixelRatio || window.devicePixelRatio;
this.id = options.id;
this.staticPlot = !!options.staticPlot;
this.scrollZoom = this.graphDiv._context.scrollZoom;
this.scrollZoom = this.graphDiv._context._scrollZoom.cartesian;

this.fullData = null;
this.updateRefs(fullLayout);
Expand Down
3 changes: 3 additions & 0 deletions src/plots/gl3d/camera.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,6 +48,7 @@ function createCamera(element, options) {

var camera = {
keyBindingMode: 'rotate',
enableWheel: true,
Comment thread
archmoj marked this conversation as resolved.
view: view,
element: element,
delay: options.delay || 16,
Expand DownExpand Up@@ -257,7 +258,9 @@ function createCamera(element, options) {
}

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

var flipX = camera.flipX ? 1 : -1;
var flipY = camera.flipY ? 1 : -1;
Expand Down
14 changes: 10 additions & 4 deletions src/plots/gl3d/scene.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -227,8 +227,15 @@ function initializeGLPlot(scene, canvas, gl) {
scene.graphDiv.emit('plotly_relayout', update);
};

scene.glplot.canvas.addEventListener('mouseup', relayoutCallback.bind(null, scene));
scene.glplot.canvas.addEventListener('wheel', relayoutCallback.bind(null, scene), passiveSupported ? {passive: false} : false);
scene.glplot.canvas.addEventListener('mouseup', function() {
relayoutCallback(scene);
});

scene.glplot.canvas.addEventListener('wheel', function() {
if(gd._context._scrollZoom.gl3d) {
relayoutCallback(scene);
}
}, passiveSupported ? {passive: false} : false);
Comment on lines +234 to +238

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've disabled scrollZoom in the plot config, but the mouse is still getting captured when scrolling by the canvas. Looks like it's because of the wheel event listener that's being registered here anyway. After I remove the wheel listener on scene element from chrome inspector, scrolling the page no longer gets captured by the canvas.

@etpinard, can we not add the listener at all if scrollzoom is disabled?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zfei thanks for the debugging. This comment is going to get lost here on a merged PR though, can you make a new issue for this? Or better yet a PR, looks like it's probably a pretty easy fix 😎

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @alexcjohnson.

I've never touched the Plotly codebase and haven't set up my mind to go through the learning curve yet. 😉

Will just leave it as an issue for now.


if(!scene.staticMode) {
scene.glplot.canvas.addEventListener('webglcontextlost', function(event) {
Expand DownExpand Up@@ -385,7 +392,6 @@ function computeTraceBounds(scene, trace, bounds) {
}

proto.plot = function(sceneData, fullLayout, layout) {

// Save parameters
this.plotArgs = [sceneData, fullLayout, layout];

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

// Update scene
this.glplot.update({});
Expand DownExpand Up@@ -776,7 +783,6 @@ proto.updateFx = function(dragmode, hovermode) {
fullCamera.up = zUp;
Lib.nestedProperty(layout, attr).set(zUp);
} else {

// none rotation modes [pan or zoom]
camera.keyBindingMode = dragmode;
}
Expand Down
6 changes: 6 additions & 0 deletions src/plots/mapbox/mapbox.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -258,6 +258,12 @@ proto.updateMap = function(calcData, fullLayout, resolve, reject) {
self.updateLayout(fullLayout);
self.resolveOnRender(resolve);
}

if(this.gd._context._scrollZoom.mapbox) {
map.scrollZoom.enable();
} else {
map.scrollZoom.disable();
}
};

proto.updateData = function(calcData) {
Expand Down
59 changes: 59 additions & 0 deletions test/jasmine/tests/config_test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -760,4 +760,63 @@ describe('config argument', function() {
.then(done);
});
});

describe('scrollZoom:', function() {
var gd;

beforeEach(function() { gd = createGraphDiv(); });

afterEach(destroyGraphDiv);

function plot(config) {
return Plotly.plot(gd, [], {}, config);
}

it('should fill in scrollZoom default', function(done) {
plot(undefined).then(function() {
expect(gd._context.scrollZoom).toBe('gl3d+geo+mapbox');
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, mapbox: 1});
Comment thread
archmoj marked this conversation as resolved.
expect(gd._context._scrollZoom.cartesian).toBe(undefined, 'no cartesian!');
})
.catch(failTest)
.then(done);
});

it('should fill in blank scrollZoom value', function(done) {
plot({scrollZoom: null}).then(function() {
expect(gd._context.scrollZoom).toBe(null);
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, mapbox: 1});
Comment thread
archmoj marked this conversation as resolved.
expect(gd._context._scrollZoom.cartesian).toBe(undefined, 'no cartesian!');
})
.catch(failTest)
.then(done);
});

it('should honor scrollZoom:true', function(done) {
plot({scrollZoom: true}).then(function() {
expect(gd._context.scrollZoom).toBe(true);
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, cartesian: 1, mapbox: 1});
})
.catch(failTest)
.then(done);
});

it('should honor scrollZoom:false', function(done) {
plot({scrollZoom: false}).then(function() {
expect(gd._context.scrollZoom).toBe(false);
expect(gd._context._scrollZoom).toEqual({});
})
.catch(failTest)
.then(done);
});

it('should honor scrollZoom flaglist', function(done) {
plot({scrollZoom: 'mapbox+cartesian'}).then(function() {
expect(gd._context.scrollZoom).toBe('mapbox+cartesian');
expect(gd._context._scrollZoom).toEqual({mapbox: 1, cartesian: 1});
})
.catch(failTest)
.then(done);
});
});
});
48 changes: 48 additions & 0 deletions test/jasmine/tests/geo_test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1961,4 +1961,52 @@ describe('Test geo zoom/pan/drag interactions:', function() {
.catch(failTest)
.then(done);
});

it('should respect scrollZoom config option', function(done) {
var fig = Lib.extendDeep({}, require('@mocks/geo_winkel-tripel'));
fig.layout.width = 700;
fig.layout.height = 500;
fig.layout.dragmode = 'pan';

function _assert(step, attr, proj, eventKeys) {
var msg = '[' + step + '] ';

var geoLayout = gd._fullLayout.geo;
var scale = geoLayout.projection.scale;
expect(scale).toBeCloseTo(attr[0], 1, msg + 'zoom');

var geo = geoLayout._subplot;
var _scale = geo.projection.scale();
expect(_scale).toBeCloseTo(proj[0], 0, msg + 'scale');

assertEventData(msg, eventKeys);
}

plot(fig)
.then(function() {
_assert('base', [1], [101.9], undefined);
})
.then(function() { return scroll([200, 250], [-200, -200]); })
.then(function() {
_assert('with scroll enable (by default)',
[1.3], [134.4],
['geo.projection.rotation.lon', 'geo.center.lon', 'geo.center.lat', 'geo.projection.scale']
);
})
.then(function() { return Plotly.plot(gd, [], {}, {scrollZoom: false}); })
.then(function() { return scroll([200, 250], [-200, -200]); })
.then(function() {
_assert('with scrollZoom:false', [1.3], [134.4], undefined);
})
.then(function() { return Plotly.plot(gd, [], {}, {scrollZoom: 'geo'}); })
.then(function() { return scroll([200, 250], [-200, -200]); })
.then(function() {
_assert('with scrollZoom:geo',
[1.74], [177.34],
['geo.projection.rotation.lon', 'geo.center.lon', 'geo.center.lat', 'geo.projection.scale']
);
})
.catch(failTest)
.then(done);
});
});
59 changes: 34 additions & 25 deletions test/jasmine/tests/gl3d_plot_interact_test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1097,6 +1097,11 @@ describe('Test gl3d drag and wheel interactions', function() {
}
};

function _assertAndReset(cnt) {
expect(relayoutCallback).toHaveBeenCalledTimes(cnt);
relayoutCallback.calls.reset();
}

Plotly.plot(gd, mock)
.then(function() {
relayoutCallback = jasmine.createSpy('relayoutCallback');
Expand All@@ -1115,48 +1120,32 @@ describe('Test gl3d drag and wheel interactions', function() {
return scroll(sceneTarget);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return scroll(sceneTarget2);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return drag(sceneTarget2, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return drag(sceneTarget, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

return Plotly.relayout(gd, {
'scene.dragmode': false,
'scene2.dragmode': false
});
_assertAndReset(1);
return Plotly.relayout(gd, {'scene.dragmode': false, 'scene2.dragmode': false});
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return drag(sceneTarget, [0, 0], [100, 100]);
})
.then(function() {
return drag(sceneTarget2, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(0);
_assertAndReset(0);

return Plotly.relayout(gd, {
'scene.dragmode': 'orbit',
'scene2.dragmode': 'turntable'
});
return Plotly.relayout(gd, {'scene.dragmode': 'orbit', 'scene2.dragmode': 'turntable'});
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
Expand All@@ -1168,7 +1157,27 @@ describe('Test gl3d drag and wheel interactions', function() {
return drag(sceneTarget2, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(2);
_assertAndReset(2);
return Plotly.plot(gd, [], {}, {scrollZoom: false});
})
.then(function() {
return scroll(sceneTarget);
})
.then(function() {
return scroll(sceneTarget2);
})
.then(function() {
_assertAndReset(0);
return Plotly.plot(gd, [], {}, {scrollZoom: 'gl3d'});
})
.then(function() {
return scroll(sceneTarget);
})
.then(function() {
return scroll(sceneTarget2);
})
.then(function() {
_assertAndReset(2);
})
.catch(failTest)
.then(done);
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/plot_api/plot_api.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -507,6 +507,25 @@ function setPlotContext(gd, config) {
// Check if gd has a specified widht/height to begin with
context._hasZeroHeight = context._hasZeroHeight || gd.clientHeight === 0;
context._hasZeroWidth = context._hasZeroWidth || gd.clientWidth === 0;

// fill context._scrollZoom helper to help manage scrollZoom flaglist
var szIn = context.scrollZoom;
var szOut = context._scrollZoom = {};
if(szIn === true) {
szOut.cartesian = 1;
szOut.gl3d = 1;
szOut.geo = 1;
szOut.mapbox = 1;
} else if(typeof szIn === 'string') {
var parts = szIn.split('+');
for(i = 0; i < parts.length; i++) {
szOut[parts[i]] = 1;
Comment thread
archmoj marked this conversation as resolved.
}
} else if(szIn !== false) {
szOut.gl3d = 1;
szOut.geo = 1;
szOut.mapbox = 1;
}
}

function plotLegacyPolar(gd, data, layout) {
Expand Down
13 changes: 9 additions & 4 deletions src/plot_api/plot_config.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -145,11 +145,16 @@ var configAttributes = {
},

scrollZoom: {
valType: 'boolean',
dflt: false,
valType: 'flaglist',
flags: ['cartesian', 'gl3d', 'geo', 'mapbox'],
extras: [true, false],
dflt: 'gl3d+geo+mapbox',
description: [
'Determines whether mouse wheel or two-finger scroll zooms is',
'enable. Has an effect only on cartesian subplots.'
'Determines whether mouse wheel or two-finger scroll zooms is enable.',
'Turned on by default for gl3d, geo and mapbox subplots',
'(as these subplot types do not have zoombox via pan),',
'but turned off by default for cartesian subplots.',
'Set `scrollZoom` to *false* to disable scrolling for all subplots.'
].join(' ')
},
doubleClick: {
Expand Down
2 changes: 1 addition & 1 deletion src/plots/cartesian/dragbox.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -417,7 +417,7 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
// deactivate mousewheel scrolling on embedded graphs
// devs can override this with layout._enablescrollzoom,
// but _ ensures this setting won't leave their page
if(!gd._context.scrollZoom && !gd._fullLayout._enablescrollzoom) {
if(!gd._context._scrollZoom.cartesian && !gd._fullLayout._enablescrollzoom) {
return;
}

Expand Down
3 changes: 3 additions & 0 deletions src/plots/geo/geo.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -420,6 +420,9 @@ proto.updateFx = function(fullLayout, geoLayout) {
bgRect.node().onmousedown = null;
bgRect.call(createGeoZoom(_this, geoLayout));
bgRect.on('dblclick.zoom', zoomReset);
if(!gd._context._scrollZoom.geo) {
bgRect.on('wheel.zoom', null);
}
}
else if(dragMode === 'select' || dragMode === 'lasso') {
bgRect.on('.zoom', null);
Expand Down
2 changes: 1 addition & 1 deletion src/plots/gl2d/scene2d.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,7 +38,7 @@ function Scene2D(options, fullLayout) {
this.pixelRatio = options.plotGlPixelRatio || window.devicePixelRatio;
this.id = options.id;
this.staticPlot = !!options.staticPlot;
this.scrollZoom = this.graphDiv._context.scrollZoom;
this.scrollZoom = this.graphDiv._context._scrollZoom.cartesian;

this.fullData = null;
this.updateRefs(fullLayout);
Expand Down
3 changes: 3 additions & 0 deletions src/plots/gl3d/camera.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,6 +48,7 @@ function createCamera(element, options) {

var camera = {
keyBindingMode: 'rotate',
enableWheel: true,
Comment thread
archmoj marked this conversation as resolved.
view: view,
element: element,
delay: options.delay || 16,
Expand DownExpand Up@@ -257,7 +258,9 @@ function createCamera(element, options) {
}

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

var flipX = camera.flipX ? 1 : -1;
var flipY = camera.flipY ? 1 : -1;
Expand Down
14 changes: 10 additions & 4 deletions src/plots/gl3d/scene.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -227,8 +227,15 @@ function initializeGLPlot(scene, canvas, gl) {
scene.graphDiv.emit('plotly_relayout', update);
};

scene.glplot.canvas.addEventListener('mouseup', relayoutCallback.bind(null, scene));
scene.glplot.canvas.addEventListener('wheel', relayoutCallback.bind(null, scene), passiveSupported ? {passive: false} : false);
scene.glplot.canvas.addEventListener('mouseup', function() {
relayoutCallback(scene);
});

scene.glplot.canvas.addEventListener('wheel', function() {
if(gd._context._scrollZoom.gl3d) {
relayoutCallback(scene);
}
}, passiveSupported ? {passive: false} : false);
Comment on lines +234 to +238

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've disabled scrollZoom in the plot config, but the mouse is still getting captured when scrolling by the canvas. Looks like it's because of the wheel event listener that's being registered here anyway. After I remove the wheel listener on scene element from chrome inspector, scrolling the page no longer gets captured by the canvas.

@etpinard, can we not add the listener at all if scrollzoom is disabled?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zfei thanks for the debugging. This comment is going to get lost here on a merged PR though, can you make a new issue for this? Or better yet a PR, looks like it's probably a pretty easy fix 😎

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @alexcjohnson.

I've never touched the Plotly codebase and haven't set up my mind to go through the learning curve yet. 😉

Will just leave it as an issue for now.


if(!scene.staticMode) {
scene.glplot.canvas.addEventListener('webglcontextlost', function(event) {
Expand DownExpand Up@@ -385,7 +392,6 @@ function computeTraceBounds(scene, trace, bounds) {
}

proto.plot = function(sceneData, fullLayout, layout) {

// Save parameters
this.plotArgs = [sceneData, fullLayout, layout];

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

// Update scene
this.glplot.update({});
Expand DownExpand Up@@ -776,7 +783,6 @@ proto.updateFx = function(dragmode, hovermode) {
fullCamera.up = zUp;
Lib.nestedProperty(layout, attr).set(zUp);
} else {

// none rotation modes [pan or zoom]
camera.keyBindingMode = dragmode;
}
Expand Down
6 changes: 6 additions & 0 deletions src/plots/mapbox/mapbox.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -258,6 +258,12 @@ proto.updateMap = function(calcData, fullLayout, resolve, reject) {
self.updateLayout(fullLayout);
self.resolveOnRender(resolve);
}

if(this.gd._context._scrollZoom.mapbox) {
map.scrollZoom.enable();
} else {
map.scrollZoom.disable();
}
};

proto.updateData = function(calcData) {
Expand Down
59 changes: 59 additions & 0 deletions test/jasmine/tests/config_test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -760,4 +760,63 @@ describe('config argument', function() {
.then(done);
});
});

describe('scrollZoom:', function() {
var gd;

beforeEach(function() { gd = createGraphDiv(); });

afterEach(destroyGraphDiv);

function plot(config) {
return Plotly.plot(gd, [], {}, config);
}

it('should fill in scrollZoom default', function(done) {
plot(undefined).then(function() {
expect(gd._context.scrollZoom).toBe('gl3d+geo+mapbox');
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, mapbox: 1});
Comment thread
archmoj marked this conversation as resolved.
expect(gd._context._scrollZoom.cartesian).toBe(undefined, 'no cartesian!');
})
.catch(failTest)
.then(done);
});

it('should fill in blank scrollZoom value', function(done) {
plot({scrollZoom: null}).then(function() {
expect(gd._context.scrollZoom).toBe(null);
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, mapbox: 1});
Comment thread
archmoj marked this conversation as resolved.
expect(gd._context._scrollZoom.cartesian).toBe(undefined, 'no cartesian!');
})
.catch(failTest)
.then(done);
});

it('should honor scrollZoom:true', function(done) {
plot({scrollZoom: true}).then(function() {
expect(gd._context.scrollZoom).toBe(true);
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, cartesian: 1, mapbox: 1});
})
.catch(failTest)
.then(done);
});

it('should honor scrollZoom:false', function(done) {
plot({scrollZoom: false}).then(function() {
expect(gd._context.scrollZoom).toBe(false);
expect(gd._context._scrollZoom).toEqual({});
})
.catch(failTest)
.then(done);
});

it('should honor scrollZoom flaglist', function(done) {
plot({scrollZoom: 'mapbox+cartesian'}).then(function() {
expect(gd._context.scrollZoom).toBe('mapbox+cartesian');
expect(gd._context._scrollZoom).toEqual({mapbox: 1, cartesian: 1});
})
.catch(failTest)
.then(done);
});
});
});
48 changes: 48 additions & 0 deletions test/jasmine/tests/geo_test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1961,4 +1961,52 @@ describe('Test geo zoom/pan/drag interactions:', function() {
.catch(failTest)
.then(done);
});

it('should respect scrollZoom config option', function(done) {
var fig = Lib.extendDeep({}, require('@mocks/geo_winkel-tripel'));
fig.layout.width = 700;
fig.layout.height = 500;
fig.layout.dragmode = 'pan';

function _assert(step, attr, proj, eventKeys) {
var msg = '[' + step + '] ';

var geoLayout = gd._fullLayout.geo;
var scale = geoLayout.projection.scale;
expect(scale).toBeCloseTo(attr[0], 1, msg + 'zoom');

var geo = geoLayout._subplot;
var _scale = geo.projection.scale();
expect(_scale).toBeCloseTo(proj[0], 0, msg + 'scale');

assertEventData(msg, eventKeys);
}

plot(fig)
.then(function() {
_assert('base', [1], [101.9], undefined);
})
.then(function() { return scroll([200, 250], [-200, -200]); })
.then(function() {
_assert('with scroll enable (by default)',
[1.3], [134.4],
['geo.projection.rotation.lon', 'geo.center.lon', 'geo.center.lat', 'geo.projection.scale']
);
})
.then(function() { return Plotly.plot(gd, [], {}, {scrollZoom: false}); })
.then(function() { return scroll([200, 250], [-200, -200]); })
.then(function() {
_assert('with scrollZoom:false', [1.3], [134.4], undefined);
})
.then(function() { return Plotly.plot(gd, [], {}, {scrollZoom: 'geo'}); })
.then(function() { return scroll([200, 250], [-200, -200]); })
.then(function() {
_assert('with scrollZoom:geo',
[1.74], [177.34],
['geo.projection.rotation.lon', 'geo.center.lon', 'geo.center.lat', 'geo.projection.scale']
);
})
.catch(failTest)
.then(done);
});
});
59 changes: 34 additions & 25 deletions test/jasmine/tests/gl3d_plot_interact_test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1097,6 +1097,11 @@ describe('Test gl3d drag and wheel interactions', function() {
}
};

function _assertAndReset(cnt) {
expect(relayoutCallback).toHaveBeenCalledTimes(cnt);
relayoutCallback.calls.reset();
}

Plotly.plot(gd, mock)
.then(function() {
relayoutCallback = jasmine.createSpy('relayoutCallback');
Expand All@@ -1115,48 +1120,32 @@ describe('Test gl3d drag and wheel interactions', function() {
return scroll(sceneTarget);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return scroll(sceneTarget2);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return drag(sceneTarget2, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return drag(sceneTarget, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

return Plotly.relayout(gd, {
'scene.dragmode': false,
'scene2.dragmode': false
});
_assertAndReset(1);
return Plotly.relayout(gd, {'scene.dragmode': false, 'scene2.dragmode': false});
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return drag(sceneTarget, [0, 0], [100, 100]);
})
.then(function() {
return drag(sceneTarget2, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(0);
_assertAndReset(0);

return Plotly.relayout(gd, {
'scene.dragmode': 'orbit',
'scene2.dragmode': 'turntable'
});
return Plotly.relayout(gd, {'scene.dragmode': 'orbit', 'scene2.dragmode': 'turntable'});
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
Expand All@@ -1168,7 +1157,27 @@ describe('Test gl3d drag and wheel interactions', function() {
return drag(sceneTarget2, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(2);
_assertAndReset(2);
return Plotly.plot(gd, [], {}, {scrollZoom: false});
})
.then(function() {
return scroll(sceneTarget);
})
.then(function() {
return scroll(sceneTarget2);
})
.then(function() {
_assertAndReset(0);
return Plotly.plot(gd, [], {}, {scrollZoom: 'gl3d'});
})
.then(function() {
return scroll(sceneTarget);
})
.then(function() {
return scroll(sceneTarget2);
})
.then(function() {
_assertAndReset(2);
})
.catch(failTest)
.then(done);
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/plot_api/plot_api.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -507,6 +507,25 @@ function setPlotContext(gd, config) {
// Check if gd has a specified widht/height to begin with
context._hasZeroHeight = context._hasZeroHeight || gd.clientHeight === 0;
context._hasZeroWidth = context._hasZeroWidth || gd.clientWidth === 0;

// fill context._scrollZoom helper to help manage scrollZoom flaglist
var szIn = context.scrollZoom;
var szOut = context._scrollZoom = {};
if(szIn === true) {
szOut.cartesian = 1;
szOut.gl3d = 1;
szOut.geo = 1;
szOut.mapbox = 1;
} else if(typeof szIn === 'string') {
var parts = szIn.split('+');
for(i = 0; i < parts.length; i++) {
szOut[parts[i]] = 1;
Comment thread
archmoj marked this conversation as resolved.
}
} else if(szIn !== false) {
szOut.gl3d = 1;
szOut.geo = 1;
szOut.mapbox = 1;
}
}

function plotLegacyPolar(gd, data, layout) {
Expand Down
13 changes: 9 additions & 4 deletions src/plot_api/plot_config.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -145,11 +145,16 @@ var configAttributes = {
},

scrollZoom: {
valType: 'boolean',
dflt: false,
valType: 'flaglist',
flags: ['cartesian', 'gl3d', 'geo', 'mapbox'],
extras: [true, false],
dflt: 'gl3d+geo+mapbox',
description: [
'Determines whether mouse wheel or two-finger scroll zooms is',
'enable. Has an effect only on cartesian subplots.'
'Determines whether mouse wheel or two-finger scroll zooms is enable.',
'Turned on by default for gl3d, geo and mapbox subplots',
'(as these subplot types do not have zoombox via pan),',
'but turned off by default for cartesian subplots.',
'Set `scrollZoom` to *false* to disable scrolling for all subplots.'
].join(' ')
},
doubleClick: {
Expand Down
2 changes: 1 addition & 1 deletion src/plots/cartesian/dragbox.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -417,7 +417,7 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
// deactivate mousewheel scrolling on embedded graphs
// devs can override this with layout._enablescrollzoom,
// but _ ensures this setting won't leave their page
if(!gd._context.scrollZoom && !gd._fullLayout._enablescrollzoom) {
if(!gd._context._scrollZoom.cartesian && !gd._fullLayout._enablescrollzoom) {
return;
}

Expand Down
3 changes: 3 additions & 0 deletions src/plots/geo/geo.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -420,6 +420,9 @@ proto.updateFx = function(fullLayout, geoLayout) {
bgRect.node().onmousedown = null;
bgRect.call(createGeoZoom(_this, geoLayout));
bgRect.on('dblclick.zoom', zoomReset);
if(!gd._context._scrollZoom.geo) {
bgRect.on('wheel.zoom', null);
}
}
else if(dragMode === 'select' || dragMode === 'lasso') {
bgRect.on('.zoom', null);
Expand Down
2 changes: 1 addition & 1 deletion src/plots/gl2d/scene2d.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,7 +38,7 @@ function Scene2D(options, fullLayout) {
this.pixelRatio = options.plotGlPixelRatio || window.devicePixelRatio;
this.id = options.id;
this.staticPlot = !!options.staticPlot;
this.scrollZoom = this.graphDiv._context.scrollZoom;
this.scrollZoom = this.graphDiv._context._scrollZoom.cartesian;

this.fullData = null;
this.updateRefs(fullLayout);
Expand Down
3 changes: 3 additions & 0 deletions src/plots/gl3d/camera.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,6 +48,7 @@ function createCamera(element, options) {

var camera = {
keyBindingMode: 'rotate',
enableWheel: true,
Comment thread
archmoj marked this conversation as resolved.
view: view,
element: element,
delay: options.delay || 16,
Expand DownExpand Up@@ -257,7 +258,9 @@ function createCamera(element, options) {
}

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

var flipX = camera.flipX ? 1 : -1;
var flipY = camera.flipY ? 1 : -1;
Expand Down
14 changes: 10 additions & 4 deletions src/plots/gl3d/scene.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -227,8 +227,15 @@ function initializeGLPlot(scene, canvas, gl) {
scene.graphDiv.emit('plotly_relayout', update);
};

scene.glplot.canvas.addEventListener('mouseup', relayoutCallback.bind(null, scene));
scene.glplot.canvas.addEventListener('wheel', relayoutCallback.bind(null, scene), passiveSupported ? {passive: false} : false);
scene.glplot.canvas.addEventListener('mouseup', function() {
relayoutCallback(scene);
});

scene.glplot.canvas.addEventListener('wheel', function() {
if(gd._context._scrollZoom.gl3d) {
relayoutCallback(scene);
}
}, passiveSupported ? {passive: false} : false);
Comment on lines +234 to +238

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've disabled scrollZoom in the plot config, but the mouse is still getting captured when scrolling by the canvas. Looks like it's because of the wheel event listener that's being registered here anyway. After I remove the wheel listener on scene element from chrome inspector, scrolling the page no longer gets captured by the canvas.

@etpinard, can we not add the listener at all if scrollzoom is disabled?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zfei thanks for the debugging. This comment is going to get lost here on a merged PR though, can you make a new issue for this? Or better yet a PR, looks like it's probably a pretty easy fix 😎

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @alexcjohnson.

I've never touched the Plotly codebase and haven't set up my mind to go through the learning curve yet. 😉

Will just leave it as an issue for now.


if(!scene.staticMode) {
scene.glplot.canvas.addEventListener('webglcontextlost', function(event) {
Expand DownExpand Up@@ -385,7 +392,6 @@ function computeTraceBounds(scene, trace, bounds) {
}

proto.plot = function(sceneData, fullLayout, layout) {

// Save parameters
this.plotArgs = [sceneData, fullLayout, layout];

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

// Update scene
this.glplot.update({});
Expand DownExpand Up@@ -776,7 +783,6 @@ proto.updateFx = function(dragmode, hovermode) {
fullCamera.up = zUp;
Lib.nestedProperty(layout, attr).set(zUp);
} else {

// none rotation modes [pan or zoom]
camera.keyBindingMode = dragmode;
}
Expand Down
6 changes: 6 additions & 0 deletions src/plots/mapbox/mapbox.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -258,6 +258,12 @@ proto.updateMap = function(calcData, fullLayout, resolve, reject) {
self.updateLayout(fullLayout);
self.resolveOnRender(resolve);
}

if(this.gd._context._scrollZoom.mapbox) {
map.scrollZoom.enable();
} else {
map.scrollZoom.disable();
}
};

proto.updateData = function(calcData) {
Expand Down
59 changes: 59 additions & 0 deletions test/jasmine/tests/config_test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -760,4 +760,63 @@ describe('config argument', function() {
.then(done);
});
});

describe('scrollZoom:', function() {
var gd;

beforeEach(function() { gd = createGraphDiv(); });

afterEach(destroyGraphDiv);

function plot(config) {
return Plotly.plot(gd, [], {}, config);
}

it('should fill in scrollZoom default', function(done) {
plot(undefined).then(function() {
expect(gd._context.scrollZoom).toBe('gl3d+geo+mapbox');
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, mapbox: 1});
Comment thread
archmoj marked this conversation as resolved.
expect(gd._context._scrollZoom.cartesian).toBe(undefined, 'no cartesian!');
})
.catch(failTest)
.then(done);
});

it('should fill in blank scrollZoom value', function(done) {
plot({scrollZoom: null}).then(function() {
expect(gd._context.scrollZoom).toBe(null);
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, mapbox: 1});
Comment thread
archmoj marked this conversation as resolved.
expect(gd._context._scrollZoom.cartesian).toBe(undefined, 'no cartesian!');
})
.catch(failTest)
.then(done);
});

it('should honor scrollZoom:true', function(done) {
plot({scrollZoom: true}).then(function() {
expect(gd._context.scrollZoom).toBe(true);
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, cartesian: 1, mapbox: 1});
})
.catch(failTest)
.then(done);
});

it('should honor scrollZoom:false', function(done) {
plot({scrollZoom: false}).then(function() {
expect(gd._context.scrollZoom).toBe(false);
expect(gd._context._scrollZoom).toEqual({});
})
.catch(failTest)
.then(done);
});

it('should honor scrollZoom flaglist', function(done) {
plot({scrollZoom: 'mapbox+cartesian'}).then(function() {
expect(gd._context.scrollZoom).toBe('mapbox+cartesian');
expect(gd._context._scrollZoom).toEqual({mapbox: 1, cartesian: 1});
})
.catch(failTest)
.then(done);
});
});
});
48 changes: 48 additions & 0 deletions test/jasmine/tests/geo_test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1961,4 +1961,52 @@ describe('Test geo zoom/pan/drag interactions:', function() {
.catch(failTest)
.then(done);
});

it('should respect scrollZoom config option', function(done) {
var fig = Lib.extendDeep({}, require('@mocks/geo_winkel-tripel'));
fig.layout.width = 700;
fig.layout.height = 500;
fig.layout.dragmode = 'pan';

function _assert(step, attr, proj, eventKeys) {
var msg = '[' + step + '] ';

var geoLayout = gd._fullLayout.geo;
var scale = geoLayout.projection.scale;
expect(scale).toBeCloseTo(attr[0], 1, msg + 'zoom');

var geo = geoLayout._subplot;
var _scale = geo.projection.scale();
expect(_scale).toBeCloseTo(proj[0], 0, msg + 'scale');

assertEventData(msg, eventKeys);
}

plot(fig)
.then(function() {
_assert('base', [1], [101.9], undefined);
})
.then(function() { return scroll([200, 250], [-200, -200]); })
.then(function() {
_assert('with scroll enable (by default)',
[1.3], [134.4],
['geo.projection.rotation.lon', 'geo.center.lon', 'geo.center.lat', 'geo.projection.scale']
);
})
.then(function() { return Plotly.plot(gd, [], {}, {scrollZoom: false}); })
.then(function() { return scroll([200, 250], [-200, -200]); })
.then(function() {
_assert('with scrollZoom:false', [1.3], [134.4], undefined);
})
.then(function() { return Plotly.plot(gd, [], {}, {scrollZoom: 'geo'}); })
.then(function() { return scroll([200, 250], [-200, -200]); })
.then(function() {
_assert('with scrollZoom:geo',
[1.74], [177.34],
['geo.projection.rotation.lon', 'geo.center.lon', 'geo.center.lat', 'geo.projection.scale']
);
})
.catch(failTest)
.then(done);
});
});
59 changes: 34 additions & 25 deletions test/jasmine/tests/gl3d_plot_interact_test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1097,6 +1097,11 @@ describe('Test gl3d drag and wheel interactions', function() {
}
};

function _assertAndReset(cnt) {
expect(relayoutCallback).toHaveBeenCalledTimes(cnt);
relayoutCallback.calls.reset();
}

Plotly.plot(gd, mock)
.then(function() {
relayoutCallback = jasmine.createSpy('relayoutCallback');
Expand All@@ -1115,48 +1120,32 @@ describe('Test gl3d drag and wheel interactions', function() {
return scroll(sceneTarget);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return scroll(sceneTarget2);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return drag(sceneTarget2, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return drag(sceneTarget, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

return Plotly.relayout(gd, {
'scene.dragmode': false,
'scene2.dragmode': false
});
_assertAndReset(1);
return Plotly.relayout(gd, {'scene.dragmode': false, 'scene2.dragmode': false});
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return drag(sceneTarget, [0, 0], [100, 100]);
})
.then(function() {
return drag(sceneTarget2, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(0);
_assertAndReset(0);

return Plotly.relayout(gd, {
'scene.dragmode': 'orbit',
'scene2.dragmode': 'turntable'
});
return Plotly.relayout(gd, {'scene.dragmode': 'orbit', 'scene2.dragmode': 'turntable'});
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
Expand All@@ -1168,7 +1157,27 @@ describe('Test gl3d drag and wheel interactions', function() {
return drag(sceneTarget2, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(2);
_assertAndReset(2);
return Plotly.plot(gd, [], {}, {scrollZoom: false});
})
.then(function() {
return scroll(sceneTarget);
})
.then(function() {
return scroll(sceneTarget2);
})
.then(function() {
_assertAndReset(0);
return Plotly.plot(gd, [], {}, {scrollZoom: 'gl3d'});
})
.then(function() {
return scroll(sceneTarget);
})
.then(function() {
return scroll(sceneTarget2);
})
.then(function() {
_assertAndReset(2);
})
.catch(failTest)
.then(done);
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/plot_api/plot_api.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -507,6 +507,25 @@ function setPlotContext(gd, config) {
// Check if gd has a specified widht/height to begin with
context._hasZeroHeight = context._hasZeroHeight || gd.clientHeight === 0;
context._hasZeroWidth = context._hasZeroWidth || gd.clientWidth === 0;

// fill context._scrollZoom helper to help manage scrollZoom flaglist
var szIn = context.scrollZoom;
var szOut = context._scrollZoom = {};
if(szIn === true) {
szOut.cartesian = 1;
szOut.gl3d = 1;
szOut.geo = 1;
szOut.mapbox = 1;
} else if(typeof szIn === 'string') {
var parts = szIn.split('+');
for(i = 0; i < parts.length; i++) {
szOut[parts[i]] = 1;
Comment thread
archmoj marked this conversation as resolved.
}
} else if(szIn !== false) {
szOut.gl3d = 1;
szOut.geo = 1;
szOut.mapbox = 1;
}
}

function plotLegacyPolar(gd, data, layout) {
Expand Down
13 changes: 9 additions & 4 deletions src/plot_api/plot_config.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -145,11 +145,16 @@ var configAttributes = {
},

scrollZoom: {
valType: 'boolean',
dflt: false,
valType: 'flaglist',
flags: ['cartesian', 'gl3d', 'geo', 'mapbox'],
extras: [true, false],
dflt: 'gl3d+geo+mapbox',
description: [
'Determines whether mouse wheel or two-finger scroll zooms is',
'enable. Has an effect only on cartesian subplots.'
'Determines whether mouse wheel or two-finger scroll zooms is enable.',
'Turned on by default for gl3d, geo and mapbox subplots',
'(as these subplot types do not have zoombox via pan),',
'but turned off by default for cartesian subplots.',
'Set `scrollZoom` to *false* to disable scrolling for all subplots.'
].join(' ')
},
doubleClick: {
Expand Down
2 changes: 1 addition & 1 deletion src/plots/cartesian/dragbox.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -417,7 +417,7 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
// deactivate mousewheel scrolling on embedded graphs
// devs can override this with layout._enablescrollzoom,
// but _ ensures this setting won't leave their page
if(!gd._context.scrollZoom && !gd._fullLayout._enablescrollzoom) {
if(!gd._context._scrollZoom.cartesian && !gd._fullLayout._enablescrollzoom) {
return;
}

Expand Down
3 changes: 3 additions & 0 deletions src/plots/geo/geo.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -420,6 +420,9 @@ proto.updateFx = function(fullLayout, geoLayout) {
bgRect.node().onmousedown = null;
bgRect.call(createGeoZoom(_this, geoLayout));
bgRect.on('dblclick.zoom', zoomReset);
if(!gd._context._scrollZoom.geo) {
bgRect.on('wheel.zoom', null);
}
}
else if(dragMode === 'select' || dragMode === 'lasso') {
bgRect.on('.zoom', null);
Expand Down
2 changes: 1 addition & 1 deletion src/plots/gl2d/scene2d.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,7 +38,7 @@ function Scene2D(options, fullLayout) {
this.pixelRatio = options.plotGlPixelRatio || window.devicePixelRatio;
this.id = options.id;
this.staticPlot = !!options.staticPlot;
this.scrollZoom = this.graphDiv._context.scrollZoom;
this.scrollZoom = this.graphDiv._context._scrollZoom.cartesian;

this.fullData = null;
this.updateRefs(fullLayout);
Expand Down
3 changes: 3 additions & 0 deletions src/plots/gl3d/camera.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,6 +48,7 @@ function createCamera(element, options) {

var camera = {
keyBindingMode: 'rotate',
enableWheel: true,
Comment thread
archmoj marked this conversation as resolved.
view: view,
element: element,
delay: options.delay || 16,
Expand DownExpand Up@@ -257,7 +258,9 @@ function createCamera(element, options) {
}

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

var flipX = camera.flipX ? 1 : -1;
var flipY = camera.flipY ? 1 : -1;
Expand Down
14 changes: 10 additions & 4 deletions src/plots/gl3d/scene.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -227,8 +227,15 @@ function initializeGLPlot(scene, canvas, gl) {
scene.graphDiv.emit('plotly_relayout', update);
};

scene.glplot.canvas.addEventListener('mouseup', relayoutCallback.bind(null, scene));
scene.glplot.canvas.addEventListener('wheel', relayoutCallback.bind(null, scene), passiveSupported ? {passive: false} : false);
scene.glplot.canvas.addEventListener('mouseup', function() {
relayoutCallback(scene);
});

scene.glplot.canvas.addEventListener('wheel', function() {
if(gd._context._scrollZoom.gl3d) {
relayoutCallback(scene);
}
}, passiveSupported ? {passive: false} : false);
Comment on lines +234 to +238

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've disabled scrollZoom in the plot config, but the mouse is still getting captured when scrolling by the canvas. Looks like it's because of the wheel event listener that's being registered here anyway. After I remove the wheel listener on scene element from chrome inspector, scrolling the page no longer gets captured by the canvas.

@etpinard, can we not add the listener at all if scrollzoom is disabled?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zfei thanks for the debugging. This comment is going to get lost here on a merged PR though, can you make a new issue for this? Or better yet a PR, looks like it's probably a pretty easy fix 😎

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @alexcjohnson.

I've never touched the Plotly codebase and haven't set up my mind to go through the learning curve yet. 😉

Will just leave it as an issue for now.


if(!scene.staticMode) {
scene.glplot.canvas.addEventListener('webglcontextlost', function(event) {
Expand DownExpand Up@@ -385,7 +392,6 @@ function computeTraceBounds(scene, trace, bounds) {
}

proto.plot = function(sceneData, fullLayout, layout) {

// Save parameters
this.plotArgs = [sceneData, fullLayout, layout];

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

// Update scene
this.glplot.update({});
Expand DownExpand Up@@ -776,7 +783,6 @@ proto.updateFx = function(dragmode, hovermode) {
fullCamera.up = zUp;
Lib.nestedProperty(layout, attr).set(zUp);
} else {

// none rotation modes [pan or zoom]
camera.keyBindingMode = dragmode;
}
Expand Down
6 changes: 6 additions & 0 deletions src/plots/mapbox/mapbox.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -258,6 +258,12 @@ proto.updateMap = function(calcData, fullLayout, resolve, reject) {
self.updateLayout(fullLayout);
self.resolveOnRender(resolve);
}

if(this.gd._context._scrollZoom.mapbox) {
map.scrollZoom.enable();
} else {
map.scrollZoom.disable();
}
};

proto.updateData = function(calcData) {
Expand Down
59 changes: 59 additions & 0 deletions test/jasmine/tests/config_test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -760,4 +760,63 @@ describe('config argument', function() {
.then(done);
});
});

describe('scrollZoom:', function() {
var gd;

beforeEach(function() { gd = createGraphDiv(); });

afterEach(destroyGraphDiv);

function plot(config) {
return Plotly.plot(gd, [], {}, config);
}

it('should fill in scrollZoom default', function(done) {
plot(undefined).then(function() {
expect(gd._context.scrollZoom).toBe('gl3d+geo+mapbox');
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, mapbox: 1});
Comment thread
archmoj marked this conversation as resolved.
expect(gd._context._scrollZoom.cartesian).toBe(undefined, 'no cartesian!');
})
.catch(failTest)
.then(done);
});

it('should fill in blank scrollZoom value', function(done) {
plot({scrollZoom: null}).then(function() {
expect(gd._context.scrollZoom).toBe(null);
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, mapbox: 1});
Comment thread
archmoj marked this conversation as resolved.
expect(gd._context._scrollZoom.cartesian).toBe(undefined, 'no cartesian!');
})
.catch(failTest)
.then(done);
});

it('should honor scrollZoom:true', function(done) {
plot({scrollZoom: true}).then(function() {
expect(gd._context.scrollZoom).toBe(true);
expect(gd._context._scrollZoom).toEqual({gl3d: 1, geo: 1, cartesian: 1, mapbox: 1});
})
.catch(failTest)
.then(done);
});

it('should honor scrollZoom:false', function(done) {
plot({scrollZoom: false}).then(function() {
expect(gd._context.scrollZoom).toBe(false);
expect(gd._context._scrollZoom).toEqual({});
})
.catch(failTest)
.then(done);
});

it('should honor scrollZoom flaglist', function(done) {
plot({scrollZoom: 'mapbox+cartesian'}).then(function() {
expect(gd._context.scrollZoom).toBe('mapbox+cartesian');
expect(gd._context._scrollZoom).toEqual({mapbox: 1, cartesian: 1});
})
.catch(failTest)
.then(done);
});
});
});
48 changes: 48 additions & 0 deletions test/jasmine/tests/geo_test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1961,4 +1961,52 @@ describe('Test geo zoom/pan/drag interactions:', function() {
.catch(failTest)
.then(done);
});

it('should respect scrollZoom config option', function(done) {
var fig = Lib.extendDeep({}, require('@mocks/geo_winkel-tripel'));
fig.layout.width = 700;
fig.layout.height = 500;
fig.layout.dragmode = 'pan';

function _assert(step, attr, proj, eventKeys) {
var msg = '[' + step + '] ';

var geoLayout = gd._fullLayout.geo;
var scale = geoLayout.projection.scale;
expect(scale).toBeCloseTo(attr[0], 1, msg + 'zoom');

var geo = geoLayout._subplot;
var _scale = geo.projection.scale();
expect(_scale).toBeCloseTo(proj[0], 0, msg + 'scale');

assertEventData(msg, eventKeys);
}

plot(fig)
.then(function() {
_assert('base', [1], [101.9], undefined);
})
.then(function() { return scroll([200, 250], [-200, -200]); })
.then(function() {
_assert('with scroll enable (by default)',
[1.3], [134.4],
['geo.projection.rotation.lon', 'geo.center.lon', 'geo.center.lat', 'geo.projection.scale']
);
})
.then(function() { return Plotly.plot(gd, [], {}, {scrollZoom: false}); })
.then(function() { return scroll([200, 250], [-200, -200]); })
.then(function() {
_assert('with scrollZoom:false', [1.3], [134.4], undefined);
})
.then(function() { return Plotly.plot(gd, [], {}, {scrollZoom: 'geo'}); })
.then(function() { return scroll([200, 250], [-200, -200]); })
.then(function() {
_assert('with scrollZoom:geo',
[1.74], [177.34],
['geo.projection.rotation.lon', 'geo.center.lon', 'geo.center.lat', 'geo.projection.scale']
);
})
.catch(failTest)
.then(done);
});
});
59 changes: 34 additions & 25 deletions test/jasmine/tests/gl3d_plot_interact_test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1097,6 +1097,11 @@ describe('Test gl3d drag and wheel interactions', function() {
}
};

function _assertAndReset(cnt) {
expect(relayoutCallback).toHaveBeenCalledTimes(cnt);
relayoutCallback.calls.reset();
}

Plotly.plot(gd, mock)
.then(function() {
relayoutCallback = jasmine.createSpy('relayoutCallback');
Expand All@@ -1115,48 +1120,32 @@ describe('Test gl3d drag and wheel interactions', function() {
return scroll(sceneTarget);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return scroll(sceneTarget2);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return drag(sceneTarget2, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return drag(sceneTarget, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

return Plotly.relayout(gd, {
'scene.dragmode': false,
'scene2.dragmode': false
});
_assertAndReset(1);
return Plotly.relayout(gd, {'scene.dragmode': false, 'scene2.dragmode': false});
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
relayoutCallback.calls.reset();

_assertAndReset(1);
return drag(sceneTarget, [0, 0], [100, 100]);
})
.then(function() {
return drag(sceneTarget2, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(0);
_assertAndReset(0);

return Plotly.relayout(gd, {
'scene.dragmode': 'orbit',
'scene2.dragmode': 'turntable'
});
return Plotly.relayout(gd, {'scene.dragmode': 'orbit', 'scene2.dragmode': 'turntable'});
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(1);
Expand All@@ -1168,7 +1157,27 @@ describe('Test gl3d drag and wheel interactions', function() {
return drag(sceneTarget2, [0, 0], [100, 100]);
})
.then(function() {
expect(relayoutCallback).toHaveBeenCalledTimes(2);
_assertAndReset(2);
return Plotly.plot(gd, [], {}, {scrollZoom: false});
})
.then(function() {
return scroll(sceneTarget);
})
.then(function() {
return scroll(sceneTarget2);
})
.then(function() {
_assertAndReset(0);
return Plotly.plot(gd, [], {}, {scrollZoom: 'gl3d'});
})
.then(function() {
return scroll(sceneTarget);
})
.then(function() {
return scroll(sceneTarget2);
})
.then(function() {
_assertAndReset(2);
})
.catch(failTest)
.then(done);
Expand Down
Loading