Mouse Scroll Wheel Zoom Rate/Amount?

Have a question about Cesium's default mouse wheel zoom. Is Cesium using the zoomIn/zoomOut methods provided on the Camera object? If so, what amount does Cesium provide to these methods when the wheel is scrolled? Is it supplying a factor of the defaultZoomAmount?

I am trying to implement external zoom control that mimicks the same behavior but, I can't seem to determine how Cesium is calculating the amount to supply to the zoomIn/zoomOut methods. I've been able to get somewhat close but its still off.

Any help would be great!

BTW, I am using Cesium 1.42.1.

Hello,

The value of Camera.defaultZoomAmount will adjust the amount the camera zooms when the user scrolls, but a few other factors go into it, like inertia, distance from the ellipsoid, and some zoom constants. See the zoomHandler function on the ScreenSpaceCameraController class for what exactly is happening under the hood.

Thanks,

Gabby

Hi Gabby

Thanks for directing me to the ZoomHandler. Seems to me though with all the complex logic that went into building the ZoomHandler and the fact that the scroll wheel makes use of it that Cesium should expose it so developers do not have to recreate the wheel for external controls. Perhaps the default behavior of the zoomIn/zoomOut functions on the Camera would perform the same behavior as the scroll wheel when nothing is passed to it…

1 Like

Hi Jason,

I see you’re point! I think the intention was to keep the zoomIn/zoomOut functions as agnostic as possible. It may be worth proposing a fix and seeing what the team thinks about it by opening a pull request if you are interested.

Thanks,

Gabby

@Fuehner Have you followed up on this? I have exactly the same issue, maybe we can coordinate a proposal for a fix?

As a workaround, it appears feasible to create a WheelEvent and dispatch it to the canvas in which the map is drawn:

const canvas = document.querySelector('canvas');
canvas.dispatchEvent(new WheelEvent('wheel', { deltaY: -100 }));  // zoom in
canvas.dispatchEvent(new WheelEvent('wheel', { deltaY: 100 }));  // zoom out

This way the actual zoom actions are handled by the ZoomHandler

I also have recently run into this issue. My users want to see a numerical representation on screen for their “zoom level.”

Right now I have a SceenSpaceEventHandler, and have an input action as follows:

zoomHandler.setInputAction(function(scroll){
zoom+=scroll;}, Cesium.ScreenSpaceEventType.WHEEL)

Where zoom is just a variable that I display on screen. This works but only if the user scrolls very slowly and only for a set amount of scrolls/zooms. Something about how Cesium syncs up scrolling on the wheel and zooming in on a 2D map gets out of sync after several scrolls or if the user scrolls too fast. Is there a better way to track this information, or fix that de-syncing?

Thanks,

Alden

Mark, have you tried anything similar to this:

zoomHandler.setInputAction(function(scroll){
zoom+=scroll;}, Cesium.ScreenSpaceEventType.WHEEL)

@Alden_Poole I’m not sure how this would apply to my use case: I create zoom in and zoom out buttons that should mimic the behavior of the user scrolling with the mouse wheel.
If I understand your code correctly this would add another way to scroll with the mouse wheel.

Ah I misread the original purpose entirely, trying to quickly add this feature and haven’t had my coffee yet.

Although, adding external controls could solve this issue for me. Are you dispatching those events in a button click?

@Alden_Poole yes, and that creates another problem, because the ‘origin’ of the zoom action is taken from the mouse position above the canvas, and with my code this position is always the position of the zoomIn or zoomOut button.
I’ve tried adding clientX and clientY properties to the event, but they seem to be ignored.

This must be what is causing my zoom “level” tracking to go haywire. I think we’re coming at this from different angles, but circling the same drain. I’ll update here if I find anyway to work around the mouse position.

1 Like

I just sumbled over this, and although I might be missing some pieces, I think that this effect might be caused by the inertiaZoom. If you haven’t already done that, you could try setting this to 0.0 and see whether it helps.

@Marco13 That didn’t fix the issue, I am certain it is because, as Mark said, that the zoom action originates from the position of the mouse. Is there a good way to override that?

Perhaps originate the zoom action from the center of the canvas?

@markchagers Try originating the zoom from the center of the screen and mapping that to a keyboard or button click?

This thread may have gotten me down the right path:

https://groups.google.com/g/cesium-dev/c/rGP29tp66Gk

@Alden_Poole Could you be a little more specific?
Currently I initiate a zoom action by creating a WheelEvent and dispatching that to the canvas. Somewhere inside the handleZoom function the zoom action gets centred to the mouse position. Neither the clientX/Y nor the screenX/Y properties of the WheelEvent seem to have any effect on this.

@markchagers Well, we have a different use case, so it is going to be a little different, but I used this chunk here:

viewer.scene.screenSpaceCameraController.enableZoom = false;

viewer.screenSpaceEventHandler.setInputAction(function(amount){
    amount = Cesium.Math.sign(amount) * viewer.scene.camera.positionCartographic.height / Math.log(viewer.scene.camera.positionCartographic.height);
    viewer.scene.camera.zoomIn(amount);
}, Cesium.ScreenSpaceEventType.WHEEL);

Since I am still using the wheel, it will look a little different. But that is the math I borrowed to find the center of the canvas and pass that in to the zoomIn action. I don’t know what kind of actions are available to you, but I followed the documentation here to set up the mousewheel event on the canvas:

https://cesium.com/learn/cesiumjs/ref-doc/ScreenSpaceEventHandler.html#getInputAction

I initially tried simulating a wheel event on the canvas, hoping to tap into the functionality of the handleZoom function, which is not currently exposed. Using Camera.zoomIn and .zoomOut directly didn’t seem to work properly: after zooming in a number of times, the map would become opaque black.
However I subsequently found that my code to calculate the amount to zoom was wrong. After I fixed that I was able to provide zoomIn and Out buttons that worked correctly by calling the camera zoom functions.
So I have now abandoned the approach outlined above.
Note: I’m not trying to replace the default scrollwheel zooming behavior, but simply to provide additional (button) controls to zoom in and out.
This is the zoom In code I now use:

const height = Cartographic.fromCartesian(this.cesiumViewer.camera.positionWC).height;
const amount = Math.min(50000, height * .5);
this.cesiumViewer.camera.zoomIn(amount);

(this.cesiumViewer is a reference to the Viewer instance)

@Alden_Poole You may get better results by listening to the camera.moveEnd event and updating your ‘zoomLevel’ display by calculating the camera height in the event handler.

I’ll give that a shot, sounds less ‘hacky’ than what I am doing right now.

Also, yes, if you keep zooming in sometimes you end up in that weird opaque black zone. Did you try setting a maximum zoom level? You can do that when you create the scene:

viewer.scene.screenSpaceCameraController.maximumZoomDistance