React-map-gl: Consider supporting transitions

Created on 15 Apr 2016  路  17Comments  路  Source: visgl/react-map-gl

At the moment the map viewport only supports the jumpTo transition as far as I can tell. Would it be possible to change this to support an option to use easeTo, flyTo or other transitions perhaps? I'd be happy to create a PR for this if it was alright.

enhancement

Most helpful comment

@vicapow Turns out using react-motion does also provide a good solution with a lot more customisability in terms of the motion. If you use something like:

<Motion style={{
  latitude: spring(viewport.latitude, { stiffness: 170, damping: 26, precision: 0.000001 }),
  longitude: spring(viewport.longitude, { stiffness: 170, damping: 26, precision: 0.000001 })
}}>
  {({ latitude, longitude }) => <MapGL
    {...viewport}
    latitude={latitude}
    longitude={longitude}
    mapStyle={mapboxStyle}
  />}
</Motion>

You can get a good smooth transition between changes in the viewport.

All 17 comments

Hi @jwarning! Good suggestion and it's been something we've thought about. One thing I've struggled with is how to enable animation for the component while still keeping the API stateless. For example, if we added a prop like flyTo, how should the component handle changes to the viewport while animating? Should it ignore updates to the viewport until the flyTo animation finishes? What if both props are updated, should we pick the viewport prop to update the map? or the flyTo prop?

With that said, I'm happy to review a diff if you're okay with it possibly not landing. At the very least, it might be a good forum to talk about the advantages and disadvantages of the approach in context with alternatives.

One other idea that's been considered is advocating for an approach that uses react-motion instead of adding it to the library, directly.

That is an interesting point @vicapow. I guess it may be something worth experimenting with a bit, but for now I'd probably be ok with the transition ending/changing if the viewport was updated during the transition.

Couldn't you delegate this to mapbox-gl with Map.easeTo?
https://www.mapbox.com/mapbox-gl-js/api/#Map.easeTo

@joewood Yeah that's the method I would use. I'm looking at potentially providing an option through the props on MapGL to set which method the _updateMapViewport call would use. Ideally this could include other transitions like flyTo, zoomTo, etc...

I have an approach working as follows for now:

<MapGL
  ...
  transitionType='easeTo'
/>

and in the underlying code:

_getTransitionType: function _getTransitionType() {
    if (this._getMap()[this.props.transitionType] !== undefined) {
      return this.props.transitionType;
    }
    return 'jumpTo';
  },
_updateMapViewport: function _updateMapViewport() {
    var state = this.state;
    if (state.latitude !== state.prevLatitude ||
      state.longitude !== state.prevLongitude ||
      state.zoom !== state.prevZoom
    ) {
      this._getMap()[this._getTransitionType()]({
        center: [state.longitude, state.latitude],
        zoom: state.zoom,
        bearing: 0,
        pitch: 0
      });
    }
    if (state.width !== state.prevWidth || state.height !== state.prevHeight) {
      this._resizeMap();
    }
  },

This approach relies on a string that corresponds to the method to use being passed in as an optional prop. It does a quick check to see if it exists, however this could in theory allow other irrelevant methods to be called. This might not matter though I guess. Thoughts? There may be a better way to do this, however I guess it depends what works in terms of keeping with the existing style of the codebase.

Actually, I thought it would be easier than that. Just use the prop as a parameter to _updateMapViewport and call that when mounting, or when receiving new props. I'll give it a go and maybe send a PR.

This works, but the issue is that you need to apply the same animation to the overlays otherwise the effect is very disjointed. The solution could be to use an easing timing function, then apply that same function to the overlay through a canvas transformation.

Another issue is that the property updates need to be gesture aware. Panning around the map using easeTo makes the map feel laggy.

Fair enough. The main use I have for this is for automatically changing the location on the map via gps data, hence you wouldn't be moving by hand. I think the specifics of what to choose and what makes the most sense in terms of usability would be up to the user. You'll welcome to try alternate solutions to the problem as well :)

@vicapow Turns out using react-motion does also provide a good solution with a lot more customisability in terms of the motion. If you use something like:

<Motion style={{
  latitude: spring(viewport.latitude, { stiffness: 170, damping: 26, precision: 0.000001 }),
  longitude: spring(viewport.longitude, { stiffness: 170, damping: 26, precision: 0.000001 })
}}>
  {({ latitude, longitude }) => <MapGL
    {...viewport}
    latitude={latitude}
    longitude={longitude}
    mapStyle={mapboxStyle}
  />}
</Motion>

You can get a good smooth transition between changes in the viewport.

@jwarning ah! that's awesome. @ibgreen think we should mention this use case in the docs?

@ibgreen think we should mention this use case in the docs?

@vicapow @jwarning
Yes absolutely. I think it would be great if react-map-gl either has a counterpart for each mapbox-gl API or a note about how to achieve the same results.

A note towards the end of the README? (e.g. a "Note on transitions" section?)

Thinking about this I got an idea. Setting up the Motion tag is still a little tricky, as the parameters are not exactly intuitive.

So, what if we created some React micro-components, corresponding to the existing mapbox-gl transition apis (we could call these components FlyTo, EaseTo, PanTo, PanBy). These components would take props that are similar to the parameters of the mapbox-gl API calls, and the render method of these components would render a react-motion component with the magic settings to implement that particular effect.

What do you think? I guess we would make react-motion a peer dependency if we don't want it included for all users.

I like the idea of adding helper components. As a first step documenting the approach, even if it is a bit verbose, will go a long way in the short term.

sorry if my comment might be off-topic.
But if you need to animate the properties, couldn't you wrap your component with something like this?

https://github.com/chenglou/react-motion

this will allow the underlying map to work the same?

@taddei That is the approach I have used and suggested in an example above. It does seem to work quite nicely and allows one to customise the style and feel of the transition as well.

@taddei @jwarning

I've added a comment recommending react-motion for transitions in the
README for the 1.0.0-beta release

I'm still thinking it would be good have some React micro-components to wrap the "raw" react-motion API into mapbox method equivalents, e.g::

import {FlyTo} from 'react-map-gl';

<FlyTo latitude={ } longitude={ }>
  {({ latitude, longitude }) =>
    <MapGL
      {...viewport}
      latitude={latitude}
      longitude={longitude}
      mapStyle={mapboxStyle}/>
  }
</FlyTo>

Would be open to considering PRs for this...

@manassra this might be something we can explore together

It seems we finally have a solution that works for react, currently planned for v3.1. Closing this as duplicate of #350.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

MattReimer picture MattReimer  路  3Comments

Majaspic picture Majaspic  路  4Comments

miccferr picture miccferr  路  4Comments

cjmyles picture cjmyles  路  3Comments

ckalas picture ckalas  路  5Comments