Cross-fade hex grid on zoom with dual-layer swap

Replace single-layer fade-out/swap/fade-in with a dual-layer (A/B)
cross-fade so old hexagons dissolve while new ones appear simultaneously.
Pans still do a direct data swap on the active layer since hex positions
are origin-anchored and stable.

Fix click/hover handlers to query both layers via queryRenderedFeatures,
and use moveLayer to keep the active layer on top so stale features from
the faded-out layer don't intercept clicks.
This commit is contained in:
Viktor Barzin 2026-02-07 22:25:50 +00:00
parent 42d34fd21a
commit 5e2c5923f6
No known key found for this signature in database
GPG key ID: 0EB088298288D958
2 changed files with 103 additions and 36 deletions

View file

@ -179,10 +179,15 @@ export function Map(props: MapProps) {
lastDataLengthRef.current = 0;
updateHeatmap();
});
mapRef.current.on('click', 'hexgrid-heatmap', function (e: mapboxgl.MapLayerMouseEvent) {
if (e.features && e.features.length > 0) {
const props = e.features[0].properties;
if (props) {
mapRef.current.on('click', function (e: mapboxgl.MapMouseEvent) {
if (!mapRef.current) return;
const layers = ['hexgrid-heatmap', 'hexgrid-heatmap-back']
.filter(l => mapRef.current!.getLayer(l));
if (layers.length === 0) return;
const features = mapRef.current.queryRenderedFeatures(e.point, { layers });
if (features.length > 0) {
const props = features[0].properties;
if (props && props.searchMinX !== undefined) {
openListingsDialog(e.lngLat.lng, e.lngLat.lat, {
minX: props.searchMinX,
minY: props.searchMinY,
@ -192,11 +197,13 @@ export function Map(props: MapProps) {
}
}
});
mapRef.current.on('mouseenter', 'hexgrid-heatmap', function () {
if (mapRef.current) mapRef.current.getCanvas().style.cursor = 'pointer';
});
mapRef.current.on('mouseleave', 'hexgrid-heatmap', function () {
if (mapRef.current) mapRef.current.getCanvas().style.cursor = '';
mapRef.current.on('mousemove', function (e: mapboxgl.MapMouseEvent) {
if (!mapRef.current) return;
const layers = ['hexgrid-heatmap', 'hexgrid-heatmap-back']
.filter(l => mapRef.current!.getLayer(l));
if (layers.length === 0) return;
const features = mapRef.current.queryRenderedFeatures(e.point, { layers });
mapRef.current.getCanvas().style.cursor = features.length > 0 ? 'pointer' : '';
});
return () => {
if (updateTimeoutRef.current) {