简体   繁体   中英

React Leaflet - Align Popup on the center of the map

If I click on a marker I want to pan the map view to the center of the marker. So far this works on my project. Furthermore I want the opened popup to be aligned on the center of the map view.

I thought I could hard-code this with the leaflet-popup class like so:

.leaflet-popup {
  transform: translate(-40%, 50%) !important;
}

But this does not work. The Problem with my code is, that the popup is positioned independently of the map view. But it should be centered according to the current view. I can show you my map setup:

EDIT

I provided a CodeSandBox link to play around. Click On the Marker on the top and you'll see that the popup is not aligned on the center of the screen:

Map Component

const {Map, TileLayer, Marker, GeoJSON} = ReactLeaflet;

function MapOverlay({setSwipeState}) {
    const mapRef = useRef(null);
    const [donationLocations] = useState([
        [48.135125, 11.581981], [58.403, 20.420], [43.300, 40],
        [70.505, -20], [40.505, -80], [-40.505, -10]
    ]);


    function centerMapView(e) {
        const {leafletElement} = mapRef.current;

        if(e) {
            leafletElement.setView(e.popup._latlng); // center view to where popup opens
            // todo: align popup in the middle of the screen
            // Get bounds of map view, divide it by 2 and apply coorditanes to the popup position


        }

    }

    function getMapData() {
        return (
            <div>
                {
                    donationLocations.map((position, i) =>
                        (
                            <Marker position={position} key={i}>
                                <MarkerPopup/>
                            </Marker>
                        )
                    )
                }
            </div>
        )
    }

    return (
        <Map
            ref={mapRef}
            center={[45.000, 10.000]}
            zoom={3}
            onPopupopen={centerMapView.bind(this)}
            zoomControl={false}
            minZoom={3}
            bounceAtZoomLimits={true}
            maxBoundsViscosity={.95}
            maxBounds={[[-180, -90], [180, 90]]}
        >
            <TileLayer
                noWrap={true}
                attribution='&amp;copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors | &amp;copy <a href="https://apps.mapbox.com/feedback/">Mapbox</a>'
                url={'https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/{z}/{x}/{y}?access_token=' + process.env.REACT_APP_MAPBOX_KEY}
            />
            {getMapData()}
        </Map>
    )
}

Marker-Popup Component

const {Popup} = ReactLeaflet;

export default function MarkerPopup() {

    return (
        <Popup
            autoPan={false} // Important part here
        >
            <Card>
                   ...
            </Card>
        </Popup>
    );
}

I managed to get it working. The Solution was to make a modal dialog that is detached from the DOM.

You can see the working code: Here

// Pass open state an setOpen function from the parent
function MarkerPopup({open, setOpen}) {
  const classes = useStyles();

  function handleClose() {
      setOpen(false);
  }

  return (
      <Popup
          autoPan={false}
      >
          <Modal
              className={classes.modal}
              open={open}
              classes={{root: classes.root}}
              onClose={handleClose.bind(this)}
              BackdropComponent={Backdrop}
              BackdropProps={{
                  timeout: 0,
                  invisible: true
              }}
          >
              <Card className={classes.card}>
                  <CardMedia
                      className={classes.media}
                      image="https://material-ui.com/static/images/cards/contemplative-reptile.jpg"
                      title="Contemplative Reptile"
                  />
                  <CardContent>
                      <Typography gutterBottom variant="h5" component="h2">
                          Lizard
                      </Typography>
                      <Typography variant="body2" color="textSecondary" component="p">
                          Lizards are a widespread group of squamate reptiles, with over 6,000 species, ranging
                          across all continents except Antarctica
                      </Typography>
                  </CardContent>
                  <CardActions>
                      <Button size="small" color="primary">
                          Details
                      </Button>
                      <Button size="small" color="primary">
                          Donate
                      </Button>
                  </CardActions>
              </Card>
          </Modal>
      </Popup>
  );
}
function centerMapView(e) {
  const { leafletElement } = mapRef.current;
  if (e) {
    const popupLatlng = e.popup._latlng;
    const zoom = leafletElement.getZoom();
    const point = leafletElement.project(popupLatlng, zoom);
    const newPoint = point.subtract([0, 180]);
    const newLatlng = leafletElement.unproject(newPoint, zoom);
    leafletElement.panTo(newLatlng, { animate: true });
  }
}

You can update your centerMapView function to the following. This will first convert your Latlng value into a point value, then modify the value by subtracting a specific amount of pixels ( offset ), finally converting the point value back to Latlng and call panTo with the value.

Best solution would be to make a modal popup that is always in the center of the screen.

Display it on the event on('popupopen')

Instead of binding popupopen to map, just call it on a marker.

marker.on('popupopen', ()=>{
   leafletElement.setView(marker.getLatLng());
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM