-1

I'm using leaflet.gesturehandling with react to support two finger scroll and ctrl+scroll-wheel with leaflet.

Testing this feature using the demo i see that on desktop all is well. On mobile however, i find the behaviour is pretty eratic. the page will scroll when trying to use two finger gestures. zooms are mixed with pans and it is just plain unpredictable. I've included my implementation which i think follows the authors guidance.

enter image description here

I'm wondering, have i done something wrong here? Do i need to disable page scrolling while two fingers are in contact with the leaflet container?

has anyone come accross this?

import React from "react"
import PropTypes from "prop-types"
import { MapContainer, TileLayer, Marker, GeoJSON } from "react-leaflet"
import "./leafletMap.css"
import * as L from "leaflet";
import { GestureHandling } from "leaflet-gesture-handling";

import "leaflet/dist/leaflet.css";
import "leaflet-gesture-handling/dist/leaflet-gesture-handling.css";

const proj4 = typeof window !== `undefined` ? require("proj4leaflet") : null

class LeafletMap extends React.Component {
  static propTypes = {
    bounds: PropTypes.array,
    startMarker: PropTypes.array,
    endMarker: PropTypes.array,
    route: PropTypes.object,
  }

  static defaultProps = {
    position: [51, -1],
    zoom: 13,
    markerText: "",
  }

  render() {
    /* Required by gatsby build, works fine witohut in develop since window is defined browser? */
    if (typeof window !== "undefined") {
      // Setup the EPSG:27700 (British National Grid) projection.
      var crs = new proj4.CRS(
        "EPSG:27700",
        "+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 +units=m +no_defs",
        {
          resolutions: [
            896.0,
            448.0,
            224.0,
            112.0,
            56.0,
            28.0,
            14.0,
            7.0,
            3.5,
            1.75,
          ],
          origin: [-238375.0, 1376256.0],
        }
      )

      // Initialize the map.
      var mapOptions = {
        crs: crs,
        minZoom: 0,
        maxZoom: 9,
        attributionControl: false,
        //gestureHandling: true
      }

      L.Map.addInitHook("addHandler", "gestureHandling", GestureHandling);

      return (
        
        <MapContainer bounds={this.props.bounds} {...mapOptions}>
          <TileLayer
            attribution='&copy; &copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
            url="https://api.os.uk/maps/raster/v1/zxy/Leisure_27700/{z}/{x}/{y}.png?key=4O65KRx7pDwdZq98W173UDKX1OSUNOAq"
          />

          <GeoJSON data={this.props.route} />
          <Marker position={this.props.startMarker} />
          <Marker position={this.props.endMarker} />
        </MapContainer>
      )
    }
    return null
  }
}

export default LeafletMap

RSmith
  • 19
  • 4

2 Answers2

3

I had some difficulties to implement leaflet-gesture-handling with react-leaflet , but here is an working exemple :

import React, { ReactElement, useEffect, useState } from "react";
import {
  MapContainer,
  Marker,
  TileLayer,
  useMap,
  useMapEvents,
} from "react-leaflet";
import {
  LatLngBounds,
  latLngBounds,
  LatLngExpression,
  LeafletEvent,
  LeafletMouseEvent,
} from "leaflet";
import { getIcon, MarkerClustor } from "@components/map/MarkerClusterGroup";
import { GestureHandling } from "leaflet-gesture-handling";
import "leaflet/dist/leaflet.css";
import "leaflet-gesture-handling/dist/leaflet-gesture-handling.css";

type MarkerType = "current" | "pg" | "pgContactList";

export interface MarkerProps {
  id: string;
  isCurrentLocation?: boolean;
  label?: ReactElement;
  position: LatLngExpression;
  type?: MarkerType;
}

export interface RegionProps {
  id: string;
  name: string;
  coords: LatLngExpression[][] | LatLngExpression[][][];
  bounds?: LatLngBounds;
}

interface MapLeafletProps {
  markers?: MarkerProps[];
  markersContactList?: MarkerProps[];
  markerCurrentLocationId?: string;
  center?: LatLngExpression;
  scrollWheelZoom?: boolean;
  autoCenter?: boolean;
  className?: string;
  onMapZoomEnd: (e: LeafletEvent) => void;
  onMapDragEnd: (e: LeafletEvent) => void;
  onMouseUp: (e: LeafletMouseEvent) => void;
  onClickMarker?: (id: string) => void;
  updateBounds?: boolean;
  zoom?: number;
}

const MapLeaflet = (props: MapLeafletProps): ReactElement => {
  const {
    markers = [],
    markersContactList = [],
    markerCurrentLocationId,
    center = [46.7111, 1.7191],
    className,
    onMapZoomEnd,
    onMapDragEnd,
    onClickMarker,
    updateBounds = false,
    zoom = 7,
  } = props;

  const [bounds, setBounds] = useState<LatLngBounds>();
  const[init, setInit] = useState<boolean>(true);


  const LocationMarker = () => {
    const map = useMapEvents({
      locationfound(e) {
        map.flyTo(e.latlng, map.getZoom());
      },
      zoomend(e) {
        onMapZoomEnd(e);
      },
      dragend(e) {
        onMapDragEnd(e);
      },
    });

    return null;
  };

  const BoundsSetter = () => {
    const map = useMap();
    console.log("Set new bounds");
    bounds && map.fitBounds(bounds);
    return null;
  };

  const GestureHandlingSetter = () => {
    /* eslint-disable */
    const map = useMap() as any;
    map.gestureHandling.enable();
    map.addHandler("gestureHandling", GestureHandling);
    setInit(false);
    /* eslint-enable */
    return null;
  };

  useEffect(() => {
    const gpMarkers = markers.filter((marker) => !marker.isCurrentLocation);
    setBounds(
      gpMarkers.length > 0
        ? latLngBounds(
            gpMarkers.map(
              (marker: MarkerProps): LatLngExpression => marker.position,
            ),
          ).pad(0.05)
        : undefined,
    );
  }, [markers]);

  const markerClusterComponent = React.useMemo(() => {
    return (
      <MarkerClustor
        markers={markers.filter((marker) => !marker.isCurrentLocation)}
        onClickMarker={onClickMarker}
        markerCurrentLocationId={markerCurrentLocationId}
      />
    );
  }, [markers, onClickMarker, markerCurrentLocationId]);

  return (
    <MapContainer
      id="map-id"
      center={center}
      zoom={zoom}
      className={className}
      scrollWheelZoom={false}
      doubleClickZoom={true}
      minZoom={2}
      preferCanvas={false}
      whenCreated={() => console.log("Map Created")}
      whenReady={() => console.log("Map Ready")}
    >
      {init && <GestureHandlingSetter />}
      <TileLayer url="https://{s}.tile.osm.org/{z}/{x}/{y}.png" noWrap={true} />
      {markerClusterComponent}
      {markersContactList.map((marker: MarkerProps, index) => (
        <Marker
          key={`marker_contact_list_${index}`}
          position={marker.position}
          icon={getIcon(marker.type, marker.id, markerCurrentLocationId)}
          eventHandlers={{
            click: () => {
              onClickMarker && onClickMarker(marker.id);
            },
          }}
        />
      ))}
      <LocationMarker />
      {updateBounds && <BoundsSetter />}
    </MapContainer>
  );
};

export default MapLeaflet;
nicolas
  • 86
  • 4
1

Not sure if you are set on using leaflet.gestureHandling for two-finger scolling solution, but here, https://stackoverflow.com/a/41631385/8543190, is another solution if you need an alternative using the leaflet interaction options https://leafletjs.com/reference-1.4.0.html#map-closepopuponclick.

t-wise
  • 41
  • 1
  • 2