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

Hover effects when layers are stacked (z-index) #5506

Closed
ethanKlein opened this issue Oct 23, 2017 · 18 comments
Closed

Hover effects when layers are stacked (z-index) #5506

ethanKlein opened this issue Oct 23, 2017 · 18 comments
Assignees
Labels

Comments

@ethanKlein
Copy link

ethanKlein commented Oct 23, 2017

Hi. 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!!

Also apologies if this is not the correct forum.

Thanks for an amazing product!

Ethan

@jfirebaugh
Copy link
Contributor

Thanks for using Mapbox, and sorry to hear you're running into an issue. In order for us to diagnose this issue, we'll need to be able to reproduce it ourselves. Could you please create a minimal self-contained JSFiddle that demonstrates the issue?

@stevage
Copy link
Contributor

stevage commented Oct 25, 2017

@dagjomar
Copy link
Contributor

@ethanKlein seems the answer from StackOverflow was to use 'mousemove' event instead combined with queryRenderedFeatures and check if the mouse is over either of the layers?

@jfirebaugh
Copy link
Contributor

I believe that the mouseenter / mouseleave approach should work. If it doesn't, please provide a code sample that demonstrates the issue. Thanks!

@yokots
Copy link

yokots commented Nov 14, 2017

@ethanKlein see here

@malwoodsantoro
Copy link
Contributor

Reopening this with a minimal test case demonstrating the issue (https://bl.ocks.org/malwoodsantoro/df063679a4d930681b806ff16e2be5e6). Hover over an unclustered point to see the flickering. It appears that the mouseleave event is firing when moving the cursor within the bounds of the icon.

@jfirebaugh
Copy link
Contributor

It looks like in this example, the mouseenter event is changing the filter on the unclustered-point-hover layer such that it causes the feature that was entered to be covered by a feature in the unclustered-point-hover layer. It's the same behavior as described in #5717 (comment), except with another layer instead of a popup.

@danswick
Copy link
Contributor

@jfirebaugh is it expected that this behavior would be different for symbol layers and fill layers? This example uses the same approach, but does not exhibit the same flickering effect - https://www.mapbox.com/mapbox-gl-js/example/hover-styles/.

I also tried moving the hover layer below the "non-hover" layer and the behavior did not change. The hover layer still flickered despite being below the "non-hover" layer.

@jfirebaugh
Copy link
Contributor

I think it's different because symbol layers have collision detection -- my previous response said "covered", but probably what's actually happening is that the new unclustered-point-hover symbol is colliding out the unclustered-point symbol, causing it to disappear. Try adding "icon-ignore-placement": true.

@malwoodsantoro
Copy link
Contributor

@jfirebaugh - @danswick and I tried your suggestion. Unfortunately, using "icon-ignore-placement": true doesn't fix the flickering.

@malwoodsantoro
Copy link
Contributor

malwoodsantoro commented Jan 5, 2018

@jfirebaugh here is a demo of the flickering when using icon-ignore-placement -

https://bl.ocks.org/malwoodsantoro/93afb2edd791f79d0b4cb17df0f9efc8

@jfirebaugh
Copy link
Contributor

@ChrisLoer Could this be related to #5887?

@ChrisLoer
Copy link
Contributor

@jfirebaugh I don't think so, although it's definitely a similar type of problem. There are no tiles reloading in this example, so the problem where the tile data becomes temporarily out of sync with the collision index doesn't exist here. Also I can reproduce the problem on v0.39 before we introduced #5887.

@mourner mourner added bug 🐞 needs investigation 🔍 Issues that require further research (e.g. it's not clear whether it's GL JS or something else) labels Mar 15, 2018
@ryanbaumann
Copy link
Contributor

ryanbaumann commented Apr 3, 2018

I'm still able to reproduce this bug in v0.44.1 and the latest master branch build on 4/3/2018.

@ChrisLoer
Copy link
Contributor

There are no tiles reloading in this example, so the problem where the tile data becomes temporarily out of sync with the collision index doesn't exist here.

😳 That was wrong. Setting the filter triggers tile reload.

I spent some time digging into the flickering map today, and I found a problem that is very closely related to #5887. The problem in this case was not that the features weren't in the CollisionIndex, but that when the tile updated with new buckets, it reset the "bucketInstanceId" (used by CrossTileSymbolIndex) for all the symbol buckets. That caused the feature to get filtered out here:

const ignoredFeatures = this.ignoredGrid.query(minX, minY, maxX, maxY);
for (let i = 0; i < ignoredFeatures.length; i++) {
if (ignoredFeatures[i].sourceID === sourceID &&
ignoredFeatures[i].tileID === tileID &&
bucketInstanceIds[ignoredFeatures[i].bucketInstanceId]) {
thisTileFeatures.push(ignoredFeatures[i].boxIndex);
}
}

We should fix this at the same time as #5887 with the same approach we used on native -- keep old data around for querying until the new collision index is ready to commit. It looks like we didn't have this specific bucketInstanceId issue on native because we don't do the same bucket filtering, but we should dig in to make sure there's not some analogous bug:

https://github.com/mapbox/mapbox-gl-native/blob/553efa38e3591ce62863d4d74222710f8e3c2337/src/mbgl/text/collision_index.cpp#L300-L306

Also I can reproduce the problem on v0.39 before we introduced #5887.

🤷‍♂️ Seems like there was probably some other cause for the same behavior in earlier versions, but I don't think it's too important to diagnose since this code has changed so much.

/cc @ansis

@ChrisLoer ChrisLoer self-assigned this Apr 3, 2018
@ChrisLoer ChrisLoer removed the needs investigation 🔍 Issues that require further research (e.g. it's not clear whether it's GL JS or something else) label Apr 3, 2018
ChrisLoer added a commit that referenced this issue Apr 5, 2018
Addresses hover flicker from issues #5887 and #5506.
Moves all data necessary for symbol querying into FeatureIndex.
Retains old FeatureIndexes if they're associated with a bucket that was used in the current placement.
ChrisLoer added a commit that referenced this issue Apr 5, 2018
Addresses hover flicker from issues #5887 and #5506.
Moves all data necessary for symbol querying into FeatureIndex.
Retains old FeatureIndexes if they're associated with a bucket that was used in the current placement.
ChrisLoer added a commit that referenced this issue Apr 5, 2018
Addresses hover flicker from issues #5887 and #5506.
Moves all data necessary for symbol querying into FeatureIndex.
Retains old FeatureIndexes if they're associated with a bucket that was used in the current placement.
@ChrisLoer
Copy link
Contributor

I have a potential fix in #6461 -- I'm not sure it's the approach we'll end up using, but it works well at least for the demo @malwoodsantoro but together in #5506 (comment). If you're interested, check out the anti-hover-flicker branch and see if it works in your situation. :)

ChrisLoer added a commit that referenced this issue Apr 6, 2018
Addresses hover flicker from issues #5887 and #5506.
Moves all data necessary for symbol querying into FeatureIndex.
Retains old FeatureIndexes if they're associated with a bucket that was used in the current placement.
ChrisLoer added a commit that referenced this issue Apr 6, 2018
Addresses hover flicker from issues #5887 and #5506.
Moves all data necessary for symbol querying into FeatureIndex.
Retains old FeatureIndexes if they're associated with a bucket that was used in the current placement.
@ChrisLoer ChrisLoer mentioned this issue Apr 11, 2018
5 tasks
ChrisLoer added a commit that referenced this issue Apr 12, 2018
Addresses hover flicker from issues #5887 and #5506.
Also fixes issue #5475/#6298, so that symbols that bleed over tile boundaries don't get missed. Under the hood, there are some good simplifications:
 - No round-tripping of viewport query coordinates through tile space
 - No more need to merge duplicate results from the same symbol showing up in multiple tiles
 - All querying-related data can now be indexed with a bucket instance id and a feature index
 - `Placement` now manages lifetime of any data needed to query against its CollisionIndex
 - CollisionBoxArray no longer involved in querying at all
ChrisLoer added a commit that referenced this issue Apr 12, 2018
Addresses hover flicker from issues #5887 and #5506.
Also fixes issue #5475/#6298, so that symbols that bleed over tile boundaries don't get missed. Under the hood, there are some good simplifications:
 - No round-tripping of viewport query coordinates through tile space
 - No more need to merge duplicate results from the same symbol showing up in multiple tiles
 - All querying-related data can now be indexed with a bucket instance id and a feature index
 - `Placement` now manages lifetime of any data needed to query against its CollisionIndex
 - CollisionBoxArray no longer involved in querying at all
ChrisLoer added a commit that referenced this issue Apr 13, 2018
Addresses hover flicker from issues #5887 and #5506.
Also fixes issue #5475/#6298, so that symbols that bleed over tile boundaries don't get missed. Under the hood, there are some good simplifications:
 - No round-tripping of viewport query coordinates through tile space
 - No more need to merge duplicate results from the same symbol showing up in multiple tiles
 - All querying-related data can now be indexed with a bucket instance id and a feature index
 - `Placement` now manages lifetime of any data needed to query against its CollisionIndex
 - CollisionBoxArray no longer involved in querying at all
ChrisLoer added a commit that referenced this issue Apr 13, 2018
Addresses hover flicker from issues #5887 and #5506.
Also fixes issue #5475/#6298, so that symbols that bleed over tile boundaries don't get missed. Under the hood, there are some good simplifications:
 - No round-tripping of viewport query coordinates through tile space
 - No more need to merge duplicate results from the same symbol showing up in multiple tiles
 - All querying-related data can now be indexed with a bucket instance id and a feature index
 - `Placement` now manages lifetime of any data needed to query against its CollisionIndex
 - CollisionBoxArray no longer involved in querying at all
@ChrisLoer
Copy link
Contributor

Fixed in #6497.

ChrisLoer added a commit that referenced this issue May 18, 2018
Addresses hover flicker from issues #5887 and #5506.
Also fixes issue #5475/#6298, so that symbols that bleed over tile boundaries don't get missed. Under the hood, there are some good simplifications:
 - No round-tripping of viewport query coordinates through tile space
 - No more need to merge duplicate results from the same symbol showing up in multiple tiles
 - All querying-related data can now be indexed with a bucket instance id and a feature index
 - `Placement` now manages lifetime of any data needed to query against its CollisionIndex
 - CollisionBoxArray no longer involved in querying at all
ChrisLoer added a commit that referenced this issue May 18, 2018
Addresses hover flicker from issues #5887 and #5506.
Also fixes issue #5475/#6298, so that symbols that bleed over tile boundaries don't get missed. Under the hood, there are some good simplifications:
 - No round-tripping of viewport query coordinates through tile space
 - No more need to merge duplicate results from the same symbol showing up in multiple tiles
 - All querying-related data can now be indexed with a bucket instance id and a feature index
 - `Placement` now manages lifetime of any data needed to query against its CollisionIndex
 - CollisionBoxArray no longer involved in querying at all
@rafasegat
Copy link

rafasegat commented Apr 12, 2021

Hey

I could fix it in an unconventional way.

For each marker I added these events:
marker.getElement().addEventListener('mouseenter', () => document.getElementById('mapbox').classList.add("cancel-tunnelling")); marker.getElement().addEventListener('mouseleave', () => document.getElementById('mapbox').classList.remove("cancel-tunnelling"));

So when the click on layer is triggered, we're testing if the element mapbox has the class 'cancel-tunnelling'
map.on('click', routeRailLineID, function (e) { if(document.getElementById('mapbox').getAttribute('class').indexOf('tunnelling') === -1) return; if(document.getElementById('mapbox').getAttribute('class').indexOf('cancel-tunnelling') > -1) return; openModal('tunnelling'); });

Now the click on Marker is not been overwritten by the Layer event.

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

No branches or pull requests