Reputation: 41
I'm struggling to create a hover effect I believe should be pretty strait-forward and also a common use case for developers using mapbox gl js.
I have a map with 2 layers. Layer 1 is a symbol layer that uses icons to mark points on the map. Layer 2 is a symbol layer that shows a score (text) on top of the icon from layer 1.
Here is a video depicting the issue
I use filtering ala https://www.mapbox.com/mapbox-gl-js/example/hover-styles/
to listen for mouse events and hide and show Layer 2 when appropriate.
In my code I'm listening for mouseenter
and mouseleave
events on Layer 1. My problem is that as Layer 2 gets shown via a change in filter, it then causes a mouseleave event to get fired from Layer 1 because Layer 2 sits above Layer 1. As the mouse moves across Layer 2 the mouseenter
and mouseleave
events for Layer 1 get continually fired as the mouse event reaches through the lines of text of Layer 2 down to Layer 1. This causes flickering of Layer 2 as it gets shown and hidden.
Things I've tried:
I've attempted using the 'before' argument https://www.mapbox.com/mapbox-gl-js/example/geojson-layer-in-stack/
I've attempted to create a new layer (Layer 3) that sits on top of Layer 1 and Layer 2 containing a transparent icon of the same size as Layer 1 and listened for mouse events on Layer 3.
I've tried toggling the visibility of Layer 2 via setStyle (rather than using filters).
I've tried including the Layer 2 content as the text-field of Layer 1 and looking for a way to show-on-hover the text-field of a mapbox layer (couldn't figure out... is this possible?)
It seems that Mapbox gl js redraws the layer order when a layer is modified via filtering or style changes. The redraw seems to always place the affected layer on top!?
Any direction or advice would be greatly appreciated. Pretty stumped!!
Thanks for reading!
Upvotes: 4
Views: 3787
Reputation: 126527
The mouseenter
event can be problematic for several reasons, including the one you noticed.
A more reliable method is to use the mousemove
event, and then directly call queryRenderedFeatures()
. Something like:
var lastFeatureId;
map.on("mousemove", function (e) {
var fs = queryRenderedFeatures(e.point, { layers: ['myiconlayer']});
if (fs.length > 0) {
f = fs[0];
if (f.id !== lastFeatureId) {
lastFeatureId = f.id;
// some visual effect now that the mouse is over a new layer.
}
}
});
Upvotes: 5