Deck.gl: Applying new viewState with a transitionDuration property causes sticky zoom/pan behaviour

Created on 19 Jul 2018  路  9Comments  路  Source: visgl/deck.gl

Please find a stripped down example at : https://codepen.io/anon/pen/QBGKmY

Hit the toggle pitch button in the top left, and you'll see the behaviour once you try to interact with the map, if you comment out line 45, the transitionDuration property of the viewState, everything will behave as expected (the pitch toggle works and all map interaction behaves as expected), of course at the cost of the transition.

 currentViewState.transitionDuration = 300; //This is the offending line, comment me out for more predictable behaviour

Detail

I have a map and data layer, all working perfectly. I have a button which toggles a "3D" mode, in this case changes the viewState.pitch and redraws the data layer with extruded: true. In the above example, I'm just toggling the pitch between 0 and 40,

This works as expected, I get the current viewState from viewManager.viewState, update the viewState with the new pitch, thensetProps on the map and deck layers and the changes are seen, this state is persisted as a user interacts with the map (pan,zoom etc.), and reverts back when the button is pressed again.

As soon as I add a transitionDuration property to the viewState when toggling the 3D mode, the behaviour breaks. The transition is animated (in this case the change in pitch). But once this transition is completed, any attempted interaction with the map, (a pan or zoom) results in the map snapping back to the state before the toggle button was clicked, as if that cached viewState is overriding any of the viewState values set by the onViewState event.

I'm not sure if this is an implementation issue on my side, but it seems odd adding this one property to the viewState so drastically changes the behaviour.

Many Thanks!

Code Example

To Do List

  • [ ] Add label and assign to milestone
  • [ ] Coding
  • [ ] Test
bug

All 9 comments

Have updated the above issue with a codepen example. Any input appreciated!

It'd be great to identify if this is a bug or an implementation issue. From the docs I couldn't find a mention of how adding this property requires additional changes, which makes me think it is a bug or a shortfall in the docs.

Thanks again

Any input on this guys? @ibgreen

Apologies if the codepen wasn't working before at some point it disabled babel, was throwing an error on the ... rest operator. All should be fine now. I've multiple animations on my app I'm holding back on due to this issue.

Many Thanks!

Any input on this guys? @ibgreen

Let's first get a comment from @Pessimistress who is the architect of the attribute transition related features.

@oller

This seems to be a scripting API specific bug - onViewStateChange callback is not invoked. Can you confirm that you are only experiencing this in Codepen?

@oller

While there is indeed a bug with the standalone bundle, there's something you need to fix with your implementation. Deck can be used as either a stateful component (without specifying viewState) or a stateless component (manage and update viewState yourself), but not mixed.

Stateful usage:

const deck = new Deck({
  // only applies till you interact with the map
  initialViewState: {longitude: -1.4157, latitude: 52.2324, zoom: 6},
  // optional callback
  onViewStateChange: ({viewState}) => {
    // you can manipulate the viewState here if needed
    return viewState;
  },
  controller: true,
  ...
});

// Never set the `viewState` prop yourself

Stateless usage:

let currentViewState = {longitude: -1.4157, latitude: 52.2324, zoom: 6};
const deck = new Deck({
  // once set, always applies till you call `setProps` again
  viewState: currentViewState,
  // required callback
  onViewStateChange: ({viewState}) => {
    // you can manipulate the viewState here if needed
    currentViewState = viewState;
    deck.setProps({viewState: currentViewState});
  },
  controller: true,
  ...
});

// invoked by user interaction outside of the map
function changeViewState() {
  currentViewState = Object.assign({}, currentViewState, {pitch: 40, transitionDuration: 1000});
  deck.setProps({viewState: currentViewState});
}

@ibgreen There are a few improvements we can make on our end:

  • [ ] Update the Deck documentation to clarify this behavior
  • [ ] When the user attempts to set viewState while using auto-control, either update the internal state or issue a warning

Hi @Pessimistress , thanks for your response! I'm experiencing this not just on codepen but in a pure-js context too, following a similar (but not identical) pattern to the codepen example.

In that instance, I'm pulling the "current" viewState from the viewManager object before I manipulate it. Some relevant snippets...

toggle3d(e) {
        const view = this;

        view.data.is3d = !view.data.is3d;

        const viewState = view.deckgl.deck.viewManager.viewState;

        if (view.data.is3d) {
            viewState.pitch = 50;
        } else {
            viewState.pitch = 0;
        }

        // viewState.transitionDuration = 300;

        const props = {
            viewState
        };

        // Update pitch properties of map layer
        view.deckgl.deck.setProps(props);
        view.deckgl.map.setProps(props);

        // Force redraw to enable 3d and update pitch property of data layer
        view.renderPostCodeLayer();
        view.renderPlacementLayer();
    },

And this is where the deckgl and map instances are created:


        view.deckgl.map = new Mapbox({
            container: view.$el.find(".js-geomap")[0],
            style: `https://maps.tilehosting.com/styles/${mapTilerMapStyle}/style.json?key=${mapTilerKey}`,
            viewState: view.deckgl.initialViewState
        });

        view.deckgl.deck = new Deck({
            canvas: view.$el.find(".js-geomap-deck")[0],
            width: "100%",
            height: "100%",
            viewState: view.deckgl.initialViewState,
            controller: true,
            onViewStateChange: ({ viewState }) => {
                // console.log("viewstate change", viewState);
                view.deckgl.deck.setProps({
                    viewState
                });
                view.deckgl.map.setProps({
                    viewState
                });
            }
        });

Which to me looks like I'm following the stateless usage pattern you'd described above. But I get the same exact sticky/broken behaviour as the codepen, only when I add in that transitionDuration property to the viewState, I setProps on.

It seems odd as my implementation was working fine until I added in that single transitionDuration property?

Could it be this bug is occurring on this app too? I'm on 6.0.1

Thanks again for your time!

@oller

  • I advise against accessing the viewManager - it is considered internal to deck and the implementation may change at any time.
  • I spent some more time looking into this. Looks like there is another bug in the ViewManager during viewport transition. We failed to catch it because we only tested the feature in React, in which view state is updated through setState and always async. In pure-js, view state is updated synchronously. I'll get a fix out soon.

@oller just published 6.0.2, looks like your Codepen is working now.

It does indeed, works on my app too - thanks again for addressing this so promptly and for such a great library!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nagix picture nagix  路  3Comments

ibesora picture ibesora  路  4Comments

ctriley picture ctriley  路  3Comments

jianhuang01 picture jianhuang01  路  3Comments

euzu picture euzu  路  3Comments