elmarquis / Leaflet.GestureHandling

Brings the basic functionality of Google Maps Gesture Handling into Leaflet. Prevents users from getting trapped on the map when scrolling a long page.
MIT License
268 stars 60 forks source link

two finger drag / page scroll prevention and glitchy panning #65

Open robertbsmith opened 3 years ago

robertbsmith commented 3 years ago

Hi Guys

I'm getting pretty poor performance using this on mobile. I created a stackoverflow post here but did not any inputs.

Post copied below:

"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.

see screengrab 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"

![](https://i.stack.imgur.com/sBds8.gif)

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
gofr commented 3 years ago

I'm also running into this.

When you start dragging around with two fingers, _handleTouch gets called.

Specifically, this line enables interaction with the map: https://github.com/elmarquis/Leaflet.GestureHandling/blob/c75fcf0f16560d4c113e5670ccb09e63ee0d30b2/src/js/leaflet-gesture-handling.js#L231

That function dynamically enables dragging and zooming and tapping. Doing this adds some CSS classes to the Leaflet container which ends up setting the touch-action CSS property on the map to none. That is used to indicate that the browser should not handle any panning or pinch-zooming because the web application will handle it. That works when the dragging etc. options are set when initializing the map, because then the CSS property is set when the map is created.

In the case of Leaflet.GestureHandling, however, the CSS property is only set while you're already in a touch event handler. That runs into the caveat mentioned on MDN:

After a gesture starts, changes to touch-action will not have any impact on the behavior of the current gesture.

So the CSS change is too late, the browser is not told not to handle the panning and the page scrolls.

In this case, the default browser action can only be prevented from happening by calling preventDefault in the event handler. If I add L.DomEvent.preventDefault(e); next to the this._enableInteractions() call I mentioned above, the default action is prevented and I get no glitchy map panning + page scrolling.