Platform: iOS
Mapbox SDK version: 3.5.0 beta 3
Using transitions via MGLStyle.transition and the respective property transitions e.g. MGLLineStyleLayer.lineOpacityTransition are inconsistent between 1) the style and property transition options and 2) the timing between forward and reverse animations for property values.
In the following examples, the traffic layer's lineOpacity should smoothly animate in and out over 1 second. In most cases, the line opacity smoothly fades over 1 second from 1.0 to 0.0, but animation from 0.0 to 1.0 occurs around twice the speed. _Is this possibly related to animation easing?_
I should note that I've increased the animation to 1.0s to exaggerate the transition effects. When using more realistic animation durations (200-300ms), there appear to not be reverse animations at all.
Likewise, these are as they appear on device: using the iOS simulator, frame rates for transitions are extremely low, maybe 4 fps, so transitions again appear to not work at all.
MGLStyle.transition from 1.0 to 0.0:_Note the accelerated transition from line opacity 0.0 to 1.0_

func setVisible(_ visible: Bool, inStyle style: MGLStyle) {
style.transition = MGLTransition(duration: 1.0, delay: 0)
for layer in style.layers {
if let layer = layer as? MGLLineStyleLayer,
layer.sourceIdentifier == "traffic" {
layer.lineOpacity = MGLStyleValue(rawValue: visible ? 1.0 : 0.0)
}
}
}
MGLStyle.transition from 1.0 to 0.1:_Same behavior as above: an accelerated transition from line opacity 0.1 to 1.0_

func setVisible(_ visible: Bool, inStyle style: MGLStyle) {
style.transition = MGLTransition(duration: 1.0, delay: 0)
for layer in style.layers {
if let layer = layer as? MGLLineStyleLayer,
layer.sourceIdentifier == "traffic" {
layer.lineOpacity = MGLStyleValue(rawValue: visible ? 1.0 : 0.1)
}
}
}
MGLLineStyleLayer.lineOpacityTransition from 1.0 to 0.0:_Note the bug here where the opacity doesn't transition from 0.0 to 1.0 at all. When toggling the layers again, the line opacity immediately jumps to 1.0 then fades back to 0.0. Using MGLStyle.transition instead of MGLLineStyleLayer.lineOpacityTransition works around the issue._
___Edit:__ transition from 0.0 to 1.0 fixed by #8440_

func setVisible(_ visible: Bool, inStyle style: MGLStyle) {
for layer in style.layers {
if let layer = layer as? MGLLineStyleLayer,
layer.sourceIdentifier == "traffic" {
layer.lineOpacityTransition = MGLTransition(duration: 1.0, delay: 0)
layer.lineOpacity = MGLStyleValue(rawValue: visible ? 1.0 : 0.0)
}
}
}
MGLLineStyleLayer.lineOpacityTransition from 1.0 to 0.1:_Note same behavior as the first two cases: the accelerated transition from line opacity 0.1 to 1.0. This is different from the behavior when animating from 0.0 to 1.0_

func setVisible(_ visible: Bool, inStyle style: MGLStyle) {
for layer in style.layers {
if let layer = layer as? MGLLineStyleLayer,
layer.sourceIdentifier == "traffic" {
layer.lineOpacityTransition = MGLTransition(duration: 1.0, delay: 0)
layer.lineOpacity = MGLStyleValue(rawValue: visible ? 1.0 : 0.1)
}
}
}
/cc @jfirebaugh @1ec5 @incanus
Here's an example showing the reverse animation choppiness with a more realistic animation duration (0.25s):

Let's get @fabian-guerra eyeballs on this too. Seems like a core bug, but could be something going haywire in the iOS translation layer.
Testing I noticed some weird behavior:


馃憜That's how I usually tested this feature by the way.
Figured out what's happening here, and it's in core.
When LineOpacity is set to 0, Style::recalculate when calling evaluate on the LineLayer results in a RenderPass::None for the layer due to this check.
The lack of a render pass for the layer circumvents updating the style's hasPendingTransitions here, which is the backing to external Style::hasTransitions().
Since we haven't recorded any in-progress transitions, we never get another Update::RecalculateStyle to feed back into the first step, which never updates the value along the transition.
If you change the viewport as @fabian-guerra noticed or you update a property value (triggering Style::onLayerPaintPropertyChanged() or related observers), that will get Update::RecalculateStyle back into the mix, immediately popping the value to the current transition state. Since in these examples we don't do that until such time as the fade in transition would have completed, we see things immediately jump to full opacity as part of the style recalculation.
Rinse and repeat.
Here is a good diff in the iosapp for testing this akin to what @ericrwolfe was doing above in Swift.
Actually I should check the other scenarios beyond just the fade in bug, but I'm pretty certain they'll be impacted by this.
So the per-property transitions are definitely fixed by #8433, but the style-wide ones do not appear to be.
Ran some tests with @ericrwolfe and confirmed that the style-wide transition issues are merely a matter of perception at relatively short durations. When done over e.g. five seconds, the interpolated values plot like so:

Which are clean easing curves.
However, there is a separate, non-critical problem. Regardless of duration, we use a constant, relatively low number of interpolated values, so the shorter a duration, the more abrupt the animation. @ericrwolfe will ticket that separately, as well as possibly customizing timing curves with e.g. CAMediaTimingFunction.
Fixed in core in https://github.com/mapbox/mapbox-gl-native/pull/8440 and applied to the next combined mobile release branch.
Reopening after further investigation around #8455:
Transitions over short durations (<1s, e.g. 0.2s) were still inconsistent even using symmetric easing curves as opposed to ease-out (https://github.com/mapbox/mapbox-gl-native/issues/8433#issuecomment-287222034), despite running on device with reasonably high frame rates (25fps), indicating this might not actually be an issue with misperception.
I ran some tests with "linear" easing instead of "ease-out" by changing include/mbgl/util/constants.hpp#L46 from
constexpr UnitBezier DEFAULT_TRANSITION_EASE = { 0, 0, 0.25, 1 };
to
constexpr UnitBezier DEFAULT_TRANSITION_EASE = { 0, 0, 1, 1 };
Here's the graph after testing a transition of lineOpacity from 0 to 1 with a duration of 0.2 seconds, 10x forwards and backwards:

A perfect graph of the linear transition should have straight lines running diagonally from corner to corner. However, as the graph shows, the transitions still follow a curve similar to ease-out, and the transition is mostly over at 0.125s instead of 0.2s (30-40% earlier than it should).
Stretching out the transition to 5 seconds produces the following graph, which rules out any issue with the easing function itself:

Perhaps there's a deeper issue with precision at these shorter transition durations?
Moving to v3.6.0 for now.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
This issue has been automatically detected as stale because it has not had recent activity and will be archived. Thank you for your contributions.