Skip to content
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

Track popups to mouse cursor #7777

Closed
peterqliu opened this issue Jan 15, 2019 · 8 comments
Closed

Track popups to mouse cursor #7777

peterqliu opened this issue Jan 15, 2019 · 8 comments
Assignees

Comments

@peterqliu
Copy link
Contributor

peterqliu commented Jan 15, 2019

Motivation

I've seen many use cases (in Studio's data inspector, in my own demos, and from others) where we'd want a popup "inspector" to follow the mouse, rather than any static lnglat. I've usually built these from scratch, but would be ideal to extend the current popup API for this.

Design Alternatives

Can simulate this with the current popup by detecting mouse position on every mousemove, projecting that to lnglat, and setLngLat to the popup. On mouseout and mouseenter of the map element, the popup would hide and show itself, respectively.

But it would be nice to one-line this.

Design/Mockup

Some API ideas:

  • popup.setLngLat({trackCursor: true}) : would expand the current method accept object literals
  • popup.trackCursor(): would add a new method to the Popup class, that along with setLngLat would mutually overwrite each other.
  • Have mouse tracking as the default behavior if setLngLat is not called. May be the easiest to implement, but weirdest to use in practice.
@ryanhamley
Copy link
Contributor

I think this would be pretty similar to the draggable marker code, the meat of which is https://github.com/mapbox/mapbox-gl-js/blob/master/src/ui/marker.js#L371-L375 It's also important to account for any offset between the cursor position and the position of the element being moved around the screen which is calculated here https://github.com/mapbox/mapbox-gl-js/blob/master/src/ui/marker.js#L441

Another use case for this would be working with multiple, stacked fill extrusions and querying them to show a popup with information as you move to various "floors" of the stacked extrusions. Could be useful for things like real estate apps or terrain applications with raster-dem tiles.

@peterqliu
Copy link
Contributor Author

I think this would be pretty similar to the draggable marker code

@ryanhamley exactly, though can we short-circuit the mouse pixel ➡️ lnglat ➡️ popup translation pixel dance, and just keep everything in px? Cursor offset straight to popup's CSS translate 🤔

@peterqliu peterqliu self-assigned this Jan 15, 2019
@ryanhamley
Copy link
Contributor

ryanhamley commented Jan 15, 2019

You mean because there wouldn't be a LatLng for the popup in the case where it's following the cursor? I think that makes sense. I just wonder if leaving latLng undefined or null would cause unexpected issues elsewhere. It's definitely something you could explore. My guess is the main argument against cutting out the conversion to/from LatLng in this case is one of consistency within the API.

@korywka
Copy link
Contributor

korywka commented Jan 18, 2019

mouse cursor popup I do this way:

popup(event) {
  const canvas = this.map.getCanvas();
  const node = document.querySelector('#map-popup');
  if (event) {
    canvas.style.cursor = 'pointer';
    node.textContent = event.features[0].properties.title;
    node.style.left = `${event.originalEvent.clientX}px`;
    node.style.top = `${event.originalEvent.clientY}px`;
    node.style.display = 'block';
  } else {
    canvas.style.cursor = '';
    node.style.display = 'none';
    node.textContent = '';
  }
}
this.map.on('mousemove', layer, event => this.popup(event));
this.map.on('mouseleave', layer, () => this.popup());

Using CSS you can center it with translate (you don't need to know actual width / height of popup node).

Example:
jan-18-2019 17-24-35

@andrewharvey
Copy link
Collaborator

Interesting, I always do it external to GL JS.

Where tooltip is a div element:

/* update the tooltip location based on where the cursor is */
window.onmousemove = function (e) {
    var x = e.clientX,
        y = e.clientY;
    tooltip.style.top = (y + 10) + 'px';
    tooltip.style.left = (x + 10) + 'px';
};

Then I watch the GL JS mousemove event and either set the tooltip display style to none or block.

Though I'm 👍 for making this a one-liner in GL JS core, it's a common use case.

@jacksonrya
Copy link

jacksonrya commented May 4, 2019

Y'all (@andrewharvey and @Bravecow) can just follow mapbox's guide to display a popup on hover, but set the popup's lngLat to the mapbox event's lngLat property.

popup.setLngLat(e.lngLat)
.setHTML(description)
.addTo(map);

Should this be included as a hint in the official example, rather than a completely new implementation?

@ryanhamley
Copy link
Contributor

@peterqliu your PR for this fix just needs to have a couple of conflicts resolved and then it can be merged and this issue can be closed

@peterqliu
Copy link
Contributor Author

#7786 🚢

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants