New event: "user movement"
Motivation
I've had a few instances where we want to keep track of whether the map's location has been moved by the user. As long as the user hasn't moved it, we want to control the map's location some other way (following an object, for instance). But as soon as the user moves the map, we want to defer to their choice.
It's not that easy right now. There are a few different events that unambiguously indicate a user movement: dragstart, rotatestart, boxzoomstart, wheel.
However, there are a few that are ambiguous: zoomstart, movestart
There isn't a simple unambiguous event fired by the user clicking on the +/- zoom buttons, for instance. (NavigationControl doesn't seem to emit events.) So a strategy of simply listening for every possible user interaction directly doesn't work.
There are workarounds (like, passing eventdata to each movement that we do initiate, and checking for its absence). But, a bit fiddly. It's still difficult to detect the difference between say mapbox-gl-geocoder firing a movement (because we can't set eventdata there), vs a user movement.
Design Alternatives
Another options might be if the map itself tracks whether its most recent movement was user-initiated.
cc @ansis
Just an aside the GeolocateControl does exactly this for the user location, this is the implementation:
https://github.com/mapbox/mapbox-gl-js/blob/12991995fa1bb62c5ecdb0282c7c142578b897e5/src/ui/control/geolocate_control.js#L417-L430
It watches movestart but checks to make sure that the event type is not from a window resize and from my testing works quite well, but as you point out doesn't distinguish user triggered vs from some other source. In the case of the geolocate control I think this is how it should work, so even a map move from the geolocate control should break the tether the GeolocateControl has on the camera.
Something similar/the same was previously requested in #9700
There isn't a simple unambiguous event fired by the user clicking on the +/- zoom buttons, for instance.
That should probably fire the zoom events then.
Yes, I need this, I do map fly and human draggings, and I want them to distinguish.
Such an event would be very useful. The fromResize trick above was not reliable enough for my needs. I ended up using this solution instead:
map.on('movestart', onMapInteraction)
function onMapInteraction(event) {
let fromUser = event.originalEvent && (event.originalEvent.type !== 'resize')
if (fromUser)
doSomething()
}
Thanks @palhal I used your idea to make sure my zoomend event callback could distinguish between a user initiated click vs a flyTo event, which in my case happens programatically.
const onMapInteraction = (e) => {
const fromUser = ['click', 'touch', 'touchend'].includes(e?.originalEvent?.type);
if (fromUser) { ... }
};