JoakimB
JoakimB

Reputation: 167

React-Map-GL: How to rise map marker to top when hover?

I am using react-map-gl and I am rendering markers within the map. I also have some hover animations going on, animations are only css so far, except when you click a marker (Not of importance for this question though).

So, this is how it looks atm: Map with markers

And on hover of a marker: Map with markers

So far it works as I want and it looks tidy.

But as you can see, in the middle of the map, I have a cluster of markers, they are close to each other. And when I hover one of them in the background it looks like this:

Map with markers

As you can see it does not place it self on top on the other markers, which is desired.

This is the CSS on hover:

.pointer {
  transform-origin: bottom center;
  width: 40px;
  position: absolute;
  display: flex;
  justify-content: center;
  align-items: center;
  bottom:0;
  transform: translateX(-50%);

  &:hover {
    animation-name: bounce;
    animation-duration: .5s;
    animation-fill-mode: forwards;
    z-index: 1000;

    + .text {
      animation-name: bounce-text;
      animation-duration: .4s;
      animation-fill-mode: forwards;
      animation-delay: .2s;
    }
  }
}

As you can see I am trying to rise the z-index to 1000 on hover, but it has no effect. Probably since each <Marker/> is absolute positioned and probably has it's own z-index stack.

Here is the Javascript for the Marker loop:

<ReactMapGl
  ref={mapEl}
  {...viewPort}
  onViewportChange={(_viewport:any) => setViewport(_viewport)}
  mapboxApiAccessToken={mapboxToken}
  mapStyle="mapbox://styles/mapbox/satellite-v9"
  style={{position:'relative', zIndex: '1'}}
>
  {mapData.markerCategories.map((markerCategory, i) => 
  
  <React.Fragment key={i}>
    {markerCategory.markers.map((marker, i) => <Marker
      latitude={marker.position[0]}
      longitude={marker.position[1]}
      key={i}
    >
        <Pointer viewPort={viewPort} marker={marker} markerCategory={markerCategory} setViewport={(viewPort:any) => setViewport(viewPort)}/>
    </Marker>)}

  </React.Fragment>
  )}

</ReactMapGl>

The <Pointer/> component is basically each graphic Icon with svg and CSS for it.

I can not find a solution of this. So my question is, how do I rise a marker to top on each hover by CSS (preferred) or JavaScript?

Upvotes: 1

Views: 3032

Answers (2)

Phiana
Phiana

Reputation: 56

With maplibre react-map-gl setup, the following simple trick solved the problem.

In App.css: (Naturally the mapbox classname variant can be interchanged with the below.)

 .maplibregl-marker:hover {
      z-index: 1000;
    }

Upvotes: 1

JoakimB
JoakimB

Reputation: 167

Ok, no massive response to this question. :) But I have solved it the "react way". Since I couldn't fix it by CSS I use a hook ([markerCategories, setMarkerCategories]) and re-structure the array of categories and markers on hover and by doing this the hovered marker gets to the top since the component is re-rendering by the changed array.

const [markerCategories, setMarkerCategories] = useState(mapData.markerCategories);

<React.Fragment key={catIndex}>
  {markerCategory.markers.map((marker, markerIndex) => <Marker
    latitude={marker.position[0]}
    longitude={marker.position[1]}
    key={markerIndex}
  >
    <Pointer 
      viewPort={viewPort} 
      marker={marker} 
      markerCategory={markerCategory} 
      setViewport={(viewPort:any) => setViewport(viewPort)}
      onMouseEnter={() => setMarkerCategories([
        ...markerCategories.filter(item => item.type !== markerCategory.type),
        {
          ...markerCategory,
          markers: [
            ...markerCategory.markers.filter((item:any) => item.position !== marker.position),
            marker
          ]
        }
      ])}
    />
  </Marker>)}

The magic lies within the onMouseEnter function where I make the category and marker/pointer last in its array. React magically re-render the markers by this new array order and the current marker gets on top. A bit messy and beautiful in the same way. :)

A bit over the top, but for future reference; This is how the mapData is structured from the start (Yes, it is just dummy data):

const mapData = {
  zoom: 12,
  position: [55.582559800905926, 13.00460428558812],
  markerCategories: [
    {
      type: 'arena',
      color: '#755728',
      markers: [
        {
          name: 'Swedbank Arena',
          position: [55.58372409722235, 12.987802928589106]
        }
      ]
    },
    {
      type: 'club',
      color: '#14366c',
      markers: [
        {
          name: 'Jockes rackabajsarlag',
          position: [55.57136374188293, 12.993493330258282]
        }
      ]
    },
    {
      type: 'lodging',
      color: '#25841e',
      markers: [
        {
          name: 'Trevligt boende',
          position: [55.564897074686044, 12.973576492024604]
        }
      ]
    },
    {
      type: 'atm',
      color: '#7f8211',
      markers: [
        {
          name: 'Minuten vid Stadion',
          position: [55.584444422651, 12.989066100435894]
        }
      ]
    },
    {
      type: 'restaurant',
      color: '#b51818',
      markers: [
        {
          name: 'O\'Leareys',
          position: [55.580453319248676, 12.958853177352454]
        },
        {
          name: 'McDonalds',
          position: [55.594068887959075, 12.9500562658861]
        },
        {
          name: 'Orvars Korvar',
          position: [55.59442766868656, 12.950861573607154]
        }
      ]
    },
    {
      type: 'parking',
      color: 'black',
      markers: [
        {
          name: 'Parkering stadion',
          position: [55.581107954130964, 12.989043415954846]
        }
      ]
    },
    {
      type: 'airplane',
      color: '#3f7d7a',
      markers: [
        {
          name: 'Arlanda',
          position: [55.590521772112474, 12.988904530774507]
        }
      ]
    },
    {
      type: 'bus',
      color: '#3f7d7a',
      markers: [
        {
          name: 'Södervärn',
          position: [55.58882552719527, 13.006218825143767]
        }
      ]
    },
    {
      type: 'boat',
      color: '#3f7d7a',
      markers: [
        {
          name: 'Malmö Hamn',
          position: [55.61658609761757, 12.997289117070949]
        }
      ]
    },
    {
      type: 'train',
      color: '#3f7d7a',
      markers: [
        {
          name: 'Malmö Centralstation',
          position: [55.60946379977733, 13.00001154238099]
        }
      ]
    },
    {
      type: 'party',
      color: '#993097',
      markers: [
        {
          name: 'Disco',
          position: [55.585603832951875, 13.04571167681943]
        }
      ]
    },
    {
      type: 'themePark',
      color: '#993097',
      markers: [
        {
          name: 'Arnes Tivoli',
          position: [55.57454396334526, 12.910502768373405]
        }
      ]
    },
    {
      type: 'swim',
      color: '#993097',
      markers: [
        {
          name: 'Kalkbrott',
          position: [55.56765833496694, 12.931152245583036]
        }
      ]
    },
    {
      type: 'misc',
      color: '#595959',
      markers: [
        {
          name: 'Kul plats!',
          position: [55.597915073658775, 12.92999260814335]
        }
      ]
    }
  ]
}

Upvotes: 1

Related Questions