Mapbox-gl-js: draggable point example is slow

Created on 25 Mar 2016  Â·  19Comments  Â·  Source: mapbox/mapbox-gl-js

mapbox-gl-js version: v0.16.0

Steps to Trigger Behavior

  1. Drag point in draggable point example http://localhost:4000/mapbox-gl-js/example/drag-a-point/

    Expected Behavior

It should be fast and smooth.

Actual Behavior

It's slow and choppy.

performance

All 19 comments

I actually can't even drag the marker...

I actually can't even drag the marker...

It's broken but fixed by https://github.com/mapbox/mapbox-gl-js/pull/2333

As @mcwhittemore points out in #2327, its lagginess depends on how much you're zoomed in. Investigating.

OK, spent some time investigating this and I found at least two issues that cause most of the lagginess:

  1. The mousedown handler needs e.originalEvent.preventDefault(). I assume this prevents Chrome (and possibly other browsers) from triggering some native drag-handling code that conflicts with point dragging.
  2. As of https://github.com/mapbox/mapbox-gl-js/commit/1f9029cce00b230ec37f9b7792916f03fc191231 by @jfirebaugh, Map setPaintProperty (which we use to change point color on hover) calls batch, which in turn always calls style._cascade, which is very expensive to do on every mousemove, especially when the map is full-screen. We should update the code to only cascade on paint property change if we change pseudo-paint properties like text-size (see #1451).

The dragging becomes much smoother if you fix both (not one or the other).

I'm also convinced that it will get much smoother if mousemove is throttled — should we do this internally for the Map mousemove event? It's a very common gotcha.

Fixing the second point can also improve Studio responsiveness considerably (cc @samanpwbb), so we should prioritize this. It may be pretty tricky though, considering upcoming DDS changes, after which cascading may be required by paint properties other than text-size and icon-size. How can we tell if a paint property needs cascade or not, without hardcoding? cc @lucaswoj

Even before 1f9029cce00b230ec37f9b7792916f03fc191231 Map#setPaintProperty called _cascade, and I don't think "call _cascade only for text-size" is the right condition. Many (most) other cases need _cascade as well -- basically, any time you change a property that should have a visual effect, you need to cascade. The only exception would be changing the property of a class that's not currently active.

We can improve cascade performance by

  • getting rid of paint classes
  • only "cascading" properties that have changed

Isn't the main issue in this example that there are two mousemove handlers, one of which is:

  • Always active, even when dragging, when it doesn't need to be
  • Always calling setPaintProperty, even when it's a no-op

Before going deeper on library-level optimizations, let's rewrite the example to have a single mousemove handler that calls setPaintProperty only when actually necessary.

After that, I'd prioritize things like this:

  • Implement delegated events, use them in this example
  • Implement frame batching like in #2343 for setData
  • Lowest priority: any setPaintProperty optimizations that make the implementation more complicated/stateful than it already is

Another thing that would be useful for this example is mouseenter/mouseleave type delegated events.

@jfirebaugh yeah, my bad about misunderstanding cascade. The main issue in the example is the preventDefault thing — it's still laggy even if you remove the laggy mousemove completely.

I also think that we should optimize noop setPaintProperty/setLayoutProperty internally — this problem came up in other examples recently too.

DOM.disableDrag doesn't help for this example, only preventDefault in mousedown does. I have no idea why. What else does it do besides disabling user selection? Could this be a Chrome bug of some sort?

looking at mapbox gl i was first puzzled by the lack of an easy way to work with markers. i then found the draggable point example which comes close to a marker, but i also get extreme lagging when zoomed into street level on chrome. with firefox zoom level doesn't seem to matter, it's sluggish no matter what, but not extreme.

in my opinion, a good user experience requires that placing and dragging markers is very responsive. it should be possible considering how smooth and responsive e.g. map zooming is.

This is much better now due to improvements in the query*Features and setData APIs.

The example is still extremely laggy in Chrome. :(

Is there work being done to improve dragging responsiveness? I'm working on an application that would benefit from very snappy drag-n-drop interactivity, and my code using mapbox is not quite able to do what I want yet. When I look at the mapbox example page, it's not very fast either, even with the minimal example. The circle still delays behind the mouse more than I'd like. I'm assuming that the problem is due to a pretty fundamental design choice where data layers need to be updated via .setData(...) which triggers quite a bit of computation/rerendering.

It's almost like there should be a setting to temporarily give a layer priority rerendering so you can do more tightly interactive animations. Or maybe others have figured out some solutions here?

I know Leaflet is completely different on the backend, but here's an example of acceptable dragging performance:
https://codepen.io/PyroStrex/pen/JKpGKv

I haven't looked into the performance recently, but the comment by @livemixlove seems to indicate the problem is still there.

There must be a way to make dragging fast. It's basic map interaction and poor performance impacts very negatively on user experience.

@emiltin , To be clear, I'm guessing that performance has improved since this issue was first created. I would consider the current drag performance to be "OK", but not great. My metric is "slower than leaflet".

Leaflet:
leaflet-drag-no-lag

example page:
mapboxgl-drag-lag

Ended up working around this by overlaying an absolutely positioned seperate canvas on top of the mapbox container with pointer-events off to be used for actively being edited features. So mapbox still handles mouse events and when you edit a shape it moves it to the active canvas and when you stop it saves/moves it back to the mapbox layer data source.

Turned out to be less painful than I thought it would be to add some of this, just use map.project(coords) to get screen x,y coordinates and your in business. Though since my issue was with mapbox gl draw I ended up having to rewrite the different draw modes for doing it this way.

The problem seemed to be much more noticeable when you have additional large vector or raster layers on top of the base map.

Also it's very related to screen resolution. You don't see the lag at all in the tiny little div on the mapbox draw demo but if you copy the code and put it in a full width/height container the lag is noticeable

Was this page helpful?
0 / 5 - 0 ratings

Related issues

yoursweater picture yoursweater  Â·  3Comments

PBrockmann picture PBrockmann  Â·  3Comments

rigoneri picture rigoneri  Â·  3Comments

aendrew picture aendrew  Â·  3Comments

stevage picture stevage  Â·  3Comments