Closed Helium314 closed 1 year ago
@Helium314 can you share a video of this?
Two videos using a testing version of StreetComplete for migration from Tangram to MapLibre:
In the first video you first see current behavior with Tangram (on the left), which I would like to have in MapLibre. Every move, unless very slow, leads to the map gliding a little bit.
On the right you see MapLibre with the move gesture detector above (there is some onMove
code which I didn't include, otherwise scrolling wouldn't work). The deceleration is clearly less "smooth" than Tangram, but using the map still feels acceptable compared to Tangram.
In the second video is MapLibre's default behavior. You can't see the taps, but basically either the move stops immediately when I take my finger off the screen, or there is this rather big "jump" with sudden acceleration and stop. This may be related to https://github.com/maplibre/maplibre-gl-js/issues/2238, which looks similar but with shorter animation.
As far as I understand, the fling ends up calling moveBy
, which sets some animation parameters that (I assume) are used for interpolation.
Then it might be enough to tune the parameters, though I'm not sure whether the starting velocity is taken into account here.
Assuming the UnitBezier in animationOptions.easing.emplace(mbgl::util::UnitBezier {0.25, 0.46, 0.45, 0.94});
is the same thing as what I can play with on https://cubic-bezier.com/#.25,.46,.45,.94:
parameter2 / parameter1
, which is basically the starting velocity of the camera animation. E.g. if we want to move by 100 pixels in 1 second, the starting speed of the animation would be 100 px / 1 s * 0.46 / 0.25 = 184 px/s
.
This means the start will look smooth if we have an initial velocity of 184 px/s. But actually we already have the velocity and can't change it, so we need to modify move distance or animation time (or both).
So e.g. we could divide the distance by 1.84 (which is close to the factor 0.5 I apply in the code above) or multiply the animationTime with 1.84. Or divide the distance by sqrt(1.84) and mulitply animationTime with sqrt(1.84) or whatever.
I tested this and all works reasonably well. [Edit: though it feels somewhat off, so I might be missing something.]0.1, 0.4, 0.45, 1
would feel better (would change the factor from 1.84 to 4), but I can't really test this.I think it would look best if the camera velocity increased linearly from zero to a maximum value and then decreased linearly to zero again. People sometimes use a quaternion slerp to interpolate the angles.
I think it would look best if the camera velocity increased linearly from zero to a maximum value and then decreased linearly to zero again.
Wouldn't this make the fling animation even more awkward? I can't imagine how a fling would look better if the camera started at velocity 0.
It's JS and not native, but also in ML JS the fling it not nice (imo actually worse than in native): Compare the fling behavior of https://demotiles.maplibre.org with other maps like https://www.openstreetmap.org, https://streetcomplete.github.io/streetcomplete-mapstyle/?provider=jawg, or https://www.google.com/maps Doing it different is not wrong by itself, but in my opinion the others do it in a better way. And in a way that is very similar, which is convenient for users who use different map sites/apps.
Thanks @Helium314 ! Let me ask around a bit and gather community feedback based on your proposal.
One thing that we can do is to keep old behaviour but also allow the behaviour you proposed.
Are you willing to contribute with a PR that will enable this ˆˆˆ?
It's JS and not native, but also in ML JS the fling it not nice (imo actually worse than in native)
GL JS gestures have been primarily optimized for non-touch input. I ran into something similar way back in mapbox/mapbox-gl-native#1266. It felt like the map had a lot of “friction” because you had to fling the map very hard to get it to move much. It used mbgl’s default animation curve but with a duration that was determined by an unnecessarily complex formula. The solution on iOS was to replace it with a much more straightforward, 1-second-long animation, matching the behavior of not only MapKit but also every standard UIKit control on the system (scroll views, menus, etc.). Applying this same duration to other gestures for zooming and rotation made the map feel a lot more fluid.
The Android map SDK subsequently updated its gesture handling in mapbox/mapbox-gl-native#553, but I don’t know if it followed the iOS implementation precisely. In any case, platform parity is not as important as matching the user’s expectations based on common controls.
I played quite a bit with different fling parameters. Here is what I found best:
The factor 1000 is because velocity is per second, animationTime in milliseconds. The factor 0.28 was found purely by testing what looks best, and may also depend on screenDensity and tiltFactor.
I don't have a phone where screenDensity is not 1.5, so can't test different screen densities.
For some reason I couldn't tilt at all (tilt gesure is enabled, but does nothing, and updating camera with tilt doesn't actually tilt), so this is untested as well.
moveBy
in in native_map_view.cpp
. As far as I understand, moveBy
is only used for the fling animation and some movement without animation, so simply adjusting parameters should not have any side effects.
MapboxConstants.ANIMATION_DURATION_FLING_BASE
from 150 to 500 ms.MapboxConstants.VELOCITY_THRESHOLD_IGNORE_FLING
seems much to high. I suggest to reduce it to something like 300.Are you willing to contribute with a PR that will enable this ˆˆˆ?
I can try... What did you have in mind? Simply switching between the 2 animation types? Or more flexibility, like configurable fling threshold and (base) animation time? And what about the UnitBezier parameters? In my opinion they are more suitable and could be used for both old and new animations.
@Helium314 I think the more flexible solution the better. But we can do this step by step maybe?
@Helium314 Can this be closed now?
Yes, done now. Thanks!
@Helium314 Thank YOU! 🙂
The current fling animation feels very awkward, and is different to basically all/most other map engines and online maps. It appears more like an easeCamera animation than a slowly decelerating continuation of the previous movement. After some testing I think the issue mainly comes from
I tried using a custom move gesture detector (see below) to implement a slightly modified fling gesture. This feels much better than current behavior, but a) still a little awkward (due to the sudden stop) and b) requires re-implementing move and pinch-zoom gestures. A first and simple step could be allowing to choose the fling velocity threshold and animation base time, but ideally the camera velocity behavior during the movement would be adjusted.