Maps: Animations are frozen

Created on 21 Jun 2019  Â·  11Comments  Â·  Source: react-native-mapbox-gl/maps

Describe the bug
All animations are frozen. They either start on some frame and never continue or dont start at all (presumably because they are at frame 0).

To Reproduce
Open the example repo and look at the PulseCircleLayer that represents the user's location or clone this repo and open the "Animated Shape" example.

Expected behavior
I expect the Pulse layer to pulse and the smiley face to pulse with a red outline in the example repo.

Screenshots.
The below image might as well be a video.
Simulator Screen Shot - iPhone X - 2019-06-21 at 10 39 31

Versions (please complete the following information):

  • Platfrom: Android and iOS
  • Device: all
  • OS: all
  • SDK Version 7.0.0-rc2
  • React Native Version 0.59.0 and 0.59.9
wontfix

Most helpful comment

Have stumbled across this issue as well on iOS Simulator and Device (haven't tested anything on Android). Couple things I learned:

  1. ShapeSource + SymbolLayer do not properly replace PointAnnotation to render custom views as they don't actually insert the React subview into the view hierarchy, but rather take a screenshot of that view and use it as static marker image, as you can see here and here.
    Effectively this means that your React views are mounted only once and captured as image, any animations in that View instance are still running somewhere it seems as I kept seeing events from that Animation, I assume that the instance is just kept around somewhere.
    Although this does not support animations, this should definitely be the recommended way to do markers as it is a huge performance boost for rendering any Geojson collection of substantial size.

  2. PointAnnotations, although actually inserting the React subview into the View hierarchy as you can see here, somehow do not update with an Animated.Value passed to them. I have no idea why this is happening, maybe somebody has an idea on what could prevent this?
    Note: I'm using react-native-reanimated in my project and tested with Animated, LayoutAnimation and Reanimated with the same results. @EricPKerr, could you give some input why it could matter to have Reanimated in the project?

Workaround

What I found gives the best result is to still use ShapeSource + SymbolLayer to render the full collection of Geojson features and use a single PointAnnotation to render the currently selected marker with a scale animation, not by passing an Animated.Value, but by using setNativeProps to update the transform property without having to re-render:

  animation = new Animated.Value(0)

  componentDidMount() {
    const { selected } = this.props

    if (selected) {
      this.animation.addListener(
        ({ value }) =>
          this.ref.current &&
          this.ref.current.setNativeProps({
            transform: [{ scale: value }]
          })
      )

      Animated.spring(this.animation, {
        tension: 100,
        toValue: 1
      }).start()
    }
  }

JSX Part

<MapboxGL.MapView {...}>
        <MapboxGL.Camera {...} />

        <MapboxGL.ShapeSource
          id="source"
          shape={{
            type: "FeatureCollection",
            features: pins.map(pin => ({
              type: "Feature",
              id: pin.id,
              properties: {
                isSelected: pin.id === this.state.selected.id
              },
              geometry: pin.geom
            }))
          }}
          onPress={({ nativeEvent: { payload } }) =>
            this.setState({ selected: payload })
          }
        >
          <MapboxGL.SymbolLayer
             id="unselectedPins"
             filter={["==", "isSelected", false]}
          >
            {/* Seems like MapboxGL needs a direct View child to render */}
            <View
              style={{
                width: 46,
                height: 46
              }}
            >
              <MapMarker color="#a4bc07" icon="🛹" />
            </View>
          </MapboxGL.SymbolLayer>
        </MapboxGL.ShapeSource>

        {this.state.selected && (
          <MapboxGL.PointAnnotation
            {/* key is required to restart the animation in componentDidMount */}
            key={this.state.selected.id}
            id={this.state.selected.id}
            coordinate={this.state.selected.geometry.coordinates}
          >
            <MapMarker selected color="#a4bc07" icon="🛹" />
          </MapboxGL.PointAnnotation>
        )}
      </MapboxGL.MapView>

Although this animation executes on the JS thread (and I would love to get this to run on the native side), this seems to work just right for me as I'm only using it as fade-in transition.

After checking Google Maps and Apple Maps, it seems that they follow a similar approach and statically render the full Geojson collection, similar to using ShapeSource + SymbolLayer, and only render one dynamic annotation, similar to PointAnnotation, when necessary.
This makes sense, and like I said before, should probably be the recommended way.

PointAnnotation Deprecation

Because PointAnnotation is the only way to actually render dynamic Views at the moment, I don't see why it should be deprecated. Also, I couldn't find any hints towards deprecation in the iOS SDK. I remember reading about some deprecations on Android, but can't pinpoint them right now.
@mfazekas or @kristfal could give some input on this?

All 11 comments

We are experiencing this issue as well.

On Friday, Jun 21, 2019 at 1:45 PM, woodpav

Describe the bug
All animations are frozen. They either start on some frame and never continue or dont start at all (presumably because they are at frame 0).

To Reproduce
Open the example repo and look at the PulseCircleLayer that represents the user's location or clone this repo (https://github.com/woodpav/maps) and open the "Animated Shape" example.

Expected behavior
I expect the Pulse layer to pulse and the smiley face to pulse with a red outline in the example repo.

Screenshots.
The below image might as well be a video.

Versions (please complete the following information):

Platfrom: Android and iOS
Device: all
OS: all
SDK Version 7.0.0-rc2
React Native Version 0.59.0 and 0.59.9

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub (https://github.com/react-native-mapbox-gl/maps/issues/155?email_source=notifications&email_token=AAG2QB4HB23XWVBEQJAWOKLP3UHTXA5CNFSM4H2TT4AKYY3PNVWWK3TUL52HS4DFUVEXG43VMWVGG33NNVSW45C7NFSM4G3AN7SA), or mute the thread (https://github.com/notifications/unsubscribe-auth/AAG2QB2RZSXB7D6MATZHTJLP3UHTXANCNFSM4H2TT4AA).

Is the current location icon not pulsating for anyone else any longer? Ours is like the one pictured above. Trying to track down where that started happening...

Would you happen to have react-native-reanimated in your project? Either directly or through something like Expo? (in node_modules)

No stock react-native project. Bug is present in the example

On both android and ios i can see pusling animation perfromed when running on a real device, on the "Animation Along a Line" example, which uses PulseCircleLayer. With the current master - a1e6925f73fc2d8f5b489d8149be6ac6616c54d1.

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.

lol not stale

Have stumbled across this issue as well on iOS Simulator and Device (haven't tested anything on Android). Couple things I learned:

  1. ShapeSource + SymbolLayer do not properly replace PointAnnotation to render custom views as they don't actually insert the React subview into the view hierarchy, but rather take a screenshot of that view and use it as static marker image, as you can see here and here.
    Effectively this means that your React views are mounted only once and captured as image, any animations in that View instance are still running somewhere it seems as I kept seeing events from that Animation, I assume that the instance is just kept around somewhere.
    Although this does not support animations, this should definitely be the recommended way to do markers as it is a huge performance boost for rendering any Geojson collection of substantial size.

  2. PointAnnotations, although actually inserting the React subview into the View hierarchy as you can see here, somehow do not update with an Animated.Value passed to them. I have no idea why this is happening, maybe somebody has an idea on what could prevent this?
    Note: I'm using react-native-reanimated in my project and tested with Animated, LayoutAnimation and Reanimated with the same results. @EricPKerr, could you give some input why it could matter to have Reanimated in the project?

Workaround

What I found gives the best result is to still use ShapeSource + SymbolLayer to render the full collection of Geojson features and use a single PointAnnotation to render the currently selected marker with a scale animation, not by passing an Animated.Value, but by using setNativeProps to update the transform property without having to re-render:

  animation = new Animated.Value(0)

  componentDidMount() {
    const { selected } = this.props

    if (selected) {
      this.animation.addListener(
        ({ value }) =>
          this.ref.current &&
          this.ref.current.setNativeProps({
            transform: [{ scale: value }]
          })
      )

      Animated.spring(this.animation, {
        tension: 100,
        toValue: 1
      }).start()
    }
  }

JSX Part

<MapboxGL.MapView {...}>
        <MapboxGL.Camera {...} />

        <MapboxGL.ShapeSource
          id="source"
          shape={{
            type: "FeatureCollection",
            features: pins.map(pin => ({
              type: "Feature",
              id: pin.id,
              properties: {
                isSelected: pin.id === this.state.selected.id
              },
              geometry: pin.geom
            }))
          }}
          onPress={({ nativeEvent: { payload } }) =>
            this.setState({ selected: payload })
          }
        >
          <MapboxGL.SymbolLayer
             id="unselectedPins"
             filter={["==", "isSelected", false]}
          >
            {/* Seems like MapboxGL needs a direct View child to render */}
            <View
              style={{
                width: 46,
                height: 46
              }}
            >
              <MapMarker color="#a4bc07" icon="🛹" />
            </View>
          </MapboxGL.SymbolLayer>
        </MapboxGL.ShapeSource>

        {this.state.selected && (
          <MapboxGL.PointAnnotation
            {/* key is required to restart the animation in componentDidMount */}
            key={this.state.selected.id}
            id={this.state.selected.id}
            coordinate={this.state.selected.geometry.coordinates}
          >
            <MapMarker selected color="#a4bc07" icon="🛹" />
          </MapboxGL.PointAnnotation>
        )}
      </MapboxGL.MapView>

Although this animation executes on the JS thread (and I would love to get this to run on the native side), this seems to work just right for me as I'm only using it as fade-in transition.

After checking Google Maps and Apple Maps, it seems that they follow a similar approach and statically render the full Geojson collection, similar to using ShapeSource + SymbolLayer, and only render one dynamic annotation, similar to PointAnnotation, when necessary.
This makes sense, and like I said before, should probably be the recommended way.

PointAnnotation Deprecation

Because PointAnnotation is the only way to actually render dynamic Views at the moment, I don't see why it should be deprecated. Also, I couldn't find any hints towards deprecation in the iOS SDK. I remember reading about some deprecations on Android, but can't pinpoint them right now.
@mfazekas or @kristfal could give some input on this?

I also got the same issue.

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.

Upgraded from 7.2.0 to 8.0.0, and now the animated CircleLayer works fine.

Was this page helpful?
0 / 5 - 0 ratings