-
-
Notifications
You must be signed in to change notification settings - Fork 769
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ability to use Map as a stateless component #1545
Comments
Thanks for opening this discussion! map.registerPreMove(() => do something here...) and then use it inside the code above, a bit similar to what we did in addProtocol. Would that improve some of the concerns? Not sure about the deltas, I'm not familiar with those unfortunately... |
I think if the usability of MapLibre GL JS in reactive frameworks would get much better, we could maybe justify a breaking change in the API... |
Thank you for raising this issue |
We have many handlers - I marked the ones currently adapted in the x/premove branch you mention, to make sure we don't miss something.
|
I honestly don't like the fact that you fire an event to get a value for something. |
Okay, so the premove should be made a middleware where we inject a function instead of fire an event. |
yup, this is my suggestion above :-) |
@birkskyum Sorry about the mixed up terms. When I said "breaking this library's API convention to have event handlers pass data back" I was referring to the Another option is to follow the convention of |
transformCameraUpdate sounds like a good direction :-) |
We already have the synchronous redraw() that will let react drive the render cycle. #300 Lines 2738 to 2754 in b7beec3
That is a good start. Maybe we should look into a mode where the asynchronous life cycle MapLibre has is completely disabled or replaced with the redraw() so we don't have to request and then stop the frames all the time. |
This seems to be similar to Deck.onBeforeRender. |
@Pessimistress , did you have a chance to look at this? I remember there was some talk in January about adding this to v3, which is getting close now, so it's a bit of a last call. |
I'll make another pass on the proposal this week. |
Closed by #2535 |
Motivation
Most modern web applications use a reactive/MVC UI framework, such as React, Angular and Vue. When building a complex application that contains a map component, it is very rare that the map library itself provides all the necessary functionalities. Some typical use cases include:
These scenarios all concern two-way state sharing between Map instances and other UI components. In the reactive programming paradigm, an exemplary implementation manages the camera state at the app's top level with a state store. Any UI component can request a change to that state. When the state does change, a rerender is triggered that flows down to the map and other components.
The design that maplibre-gl inherited from mapbox-gl does not make this easy. When the map receives user input, it immediately modifies the camera (transform) and rerenders. A
move
event is fired after the fact. Say the application updates its state based on amove
event listener. When the other React components get rendered, they are always one animation frame behind the map.I have maintained react-map-gl for over five years. There had been different attempts to address this issue, all concerning a) blocking the map from updating its own camera b) forcing the map to redraw when React rerenders. Prior to v6, the wrapper sets
interactive: false
and implements its own input handlers. In v7, the wrapper swaps outmap.transform
with a "shadow transform" that matches the React props just before repaint. Both approaches have painful shortcomings including performance and a creeping amount of hacks.mapbox-gl issue regarding the stateful nature of input handling
mapbox-gl issue regarding the state dependency of transform
Proposal
Whenever
map.transform
is about to be modified (any operation that leads to amove
event), fire apremove
event with the proposed changes:zoom
,pitch
,bearing
,center
. The event listeners are allowed to mutate the proposed camera parameters.map.transform
is then updated to the "approved" values. In pseudo code:The current usage pattern and code path will stay the same if the event is not handled.
In an application (or wrapper library) where the Map instance is used as a stateless component,
premove
is used to block the camera from updating immediately. Rather, the transform will be modified when the state change propogates.Some of the handler classes will need to be updated. They currently produce
panDelta
,pitchDelta
,zoomDelta
etc. for each input event and assume thatmap.transform
holds the accumulated change. Once the transform state and user intention are decoupled, the handlers will need to track the accumulated changes themselves, so that interaction is still responsive and not dependent on when rerender happens.Here is a very rough proof-of-concept, only enough changes for pointer inputs to work correctly:
https://github.com/maplibre/maplibre-gl-js/compare/x/premove-poc
I'm open to alternative ideas.
Concerns
The text was updated successfully, but these errors were encountered: