vue-leaflet/Vue2Leaflet

touch not fired on marker when scrollable map

bluelemonade opened this issue · 14 comments

Hi,

on a multitouch windows 10 display I have the following problem. When the div element is scrollable via touchpoints @clicks or @starttouch or whatever event isn't fired on the map.

I have integrated small buttons to move the map, they are working. where's the problem.

<LMap :ref="'homininenmap' + idNr" :tms="false" :zoom="zoom" :center="initialLocation" :minZoom="minZoom" :maxZoom="maxZoom" :options="mapOptions" :maxBounds="maxBounds" zoomControl="false" 
        @ready="mapReady"
        @moveend="mapMoveEnd" 
        @update:zoom="zoomUpdated"
        @update:center="centerUpdated"
        @update:bounds="boundsUpdated">
        <!-- <LIcondefault></LIcondefault> -->
        <LTileLayer :url="url" :tms="false" :option="tileOptions" ></LTileLayer>
        <!-- <LMarker :lat-lng="returnFundortPos" :icon="defaultIcon" @touchstart="clickMarker()" ></LMarker> -->
        <!-- Create image icon (icon) from l-icon tag -->
        

        <v-marker-cluster :options="clusterOptions" @clusterclick="click" @ready="ready">
          <!-- <LMarker v-for="l in locations" :key="l.id" :lat-lng="l.latlng" :icon="icon" icon-url="./static/map-data/marker.svg"  @touchstart="clusterMarkerClick"> -->
          <!-- <LMarker v-for="l in locations" :key="l.id" :lat-lng="l.latlng" @touchstart="clusterMarkerClick"> -->
          <LMarker v-for="l in selection" :key="l.nr" :lat-lng="[l.lat, l.lon]" >
            <!-- <LPopup @popclick="popClick" :content="l.text"></LPopup> -->
            <LIcon
              :icon-size="dynamicSize"
              :icon-anchor="dynamicAnchor"
              icon-url="./static/map-data/marker-passiv.svg"
            />
          <LTooltip :content="l.Name" :options="tooltipOptions" @touchstart="clusterMarkerClick" @click="clusterMarkerClick"></LTooltip>
          </LMarker>
        </v-marker-cluster>

        <div @touchstart="mapRight" class="leaflet-centerright">
              <div class="move-btn"><span> 🞂 </span></div>
        </div>    
         <div @touchstart="mapLeft" class="leaflet-centerleft">
              <div class="move-btn"><span> 🞀 </span></div>
        </div>
        <div @touchstart="mapUp" class="leaflet-centertop">
              <div class="move-btn"><span> 🞁 </span></div>
        </div> 
        <div @touchstart="mapDown" class="leaflet-centerbottom">
              <div class="move-btn"><span> 🞃 </span></div>
        </div>   
      
    </LMap>
mikeu commented

Hi @bluelemonade , I'm afraid I don't have access to a multitouch Windows 10 display so can't really investigate this too deeply, and I'm not sure what making a div scrollable via touchpoints means either (perhaps because I don't work with multitouch displays?), or which div you're referring to there—the one containing the map? Hopefully we can get your issue sorted out anyway though!

First thing I notice is that in your sample code, you don't actually have any handlers bound to your map for @touchstart or @click... I take it that's because you tried them and they didn't work, so you removed them? You do have a few non-standard event handlers commented out, such as @popclick which I'm not familiar with—have you added custom code or additional libraries to generate those, that might be interfering with other events as well?

You also don't share what your mapOptions look like. It's possible that would have something to do with it, but from what I can see in the Leaflet documentation the options around touch interaction don't seem to be related to disabling events, so I'd guess that's probably not it either, especially if you've already tried experimenting with the available Leaflet options.

If what you're worried about is events not firing on the markers, rather than on the map, then I have seen people run into problems with the marker cluster library before, because the markers you add to the map aren't necessarily actually there any more, depending on whether they've been replaced by a cluster marker, and so any styling, events, etc., for the object you added to the map aren't always available.

At this point I think my biggest question would be, are you able to confirm that the events you're looking for do fire from a vanilla Leaflet map?

that‘s my method for the tooltip, but it didn‘t execute

clusterMarkerClick (nr){
console.log("clusterMarkerClick", nr);
this.$emit('openFundort', nr)
},

mikeu commented

So the problem is that when the tooltip is shown, you don't see it fire an event when you touch it or click on it? That appears in your code to be the only place clusterMarkerClick is bound to an event on a component that isn't commented out. And so you don't mean that the problem is that the events aren't firing on the map, but that they aren't firing on the tooltip?

Have you been able to confirm that the events you're looking for do fire from a vanilla Leaflet map?

I'm also curious, does it start working if you simply include all of the markers on your map without clustering them?

it works now:

 <v-marker-cluster :options="clusterOptions" @ready="ready">  <!--  @click="clusterMarkerClick"  -->
          <!-- <LMarker v-for="l in locations" :key="l.id" :lat-lng="l.latlng" :icon="icon" icon-url="./static/map-data/marker.svg"  @touchstart="clusterMarkerClick"> -->
          <!-- <LMarker v-for="l in locations" :key="l.id" :lat-lng="l.latlng" @touchstart="clusterMarkerClick"> -->
          <!-- <LMarker v-for="l in fundorte" :key="l.nr" :lat-lng="[ l.lat, l.lon]" @click="clusterMarkerClick(l.nr)" @touchstart="clusterMarkerClick(l.nr)"> -->
          <LMarker v-for="l in fundorte" :key="l.nr" :lat-lng="[ l.lat, l.lon]" @touchstart="clusterMarkerClick(l.nr)" @click="clusterMarkerClick(l.nr)">
            <!-- <LPopup @popclick="popClick" :content="l.text"></LPopup> -->
            <LIcon
              :icon-size="dynamicSize"
              :icon-anchor="dynamicAnchor"
              :icon-url="dynamicIcon(l.nr)"
             
            />
          <!-- <LTooltip  :content="l[getLanguage]" :options="tooltipOptions" @click="clusterMarkerClick" @touchstart="clusterMarkerClick"></LTooltip> -->
          <LTooltip  :content="l[getLanguage]" :options="tooltipOptions" @touchstart="clusterMarkerClick(l.nr)" @click="clusterMarkerClick(l.nr)"></LTooltip>
          </LMarker>
        </v-marker-cluster>

I currently have a problem with the +- Control and my right / left / up / down Buttons that I use instead of dragging on the map. (rotated map made some problems with the drag direction)

The Visibility of the zoom UI and the other Buttons get lost depending on the rotation, seems to be a very special problem with css tranformation and index-z.

OK, I did an example with minimal code. why are the touchs on the markers irgnored? you could tet it in dev mode with touch device on. when using the @click it works.

<template>
  <div style="height: 80vh">
    <LMap :zoom="zoom" :center="center">
      <LTileLayer :url="url"></LTileLayer>
      <!-- <LMarker :lat-lng="[13.1333682,77.5651881]" @click="click" @touchstart="touch"></LMarker>
      <LMarker :lat-lng="[13.1340669,77.56707]" @click="click" @touchstart="touch"></LMarker>
      <LMarker :lat-lng="[13.1348904,77.5643231]" @click="click" @touchstart="touch"></LMarker>
      <LMarker :lat-lng="[13.1367826,77.5711133]" @click="click" @touchstart="touch"></LMarker> -->
      <LMarker :lat-lng="[13.1333682,77.5651881]" @touchstart="touch"></LMarker>
      <LMarker :lat-lng="[13.1340669,77.56707]"   @touchstart="touch"></LMarker>
      <LMarker :lat-lng="[13.1348904,77.5643231]" @touchstart="touch"></LMarker>
      <LMarker :lat-lng="[13.1367826,77.5711133]" @touchstart="touch"></LMarker>
    </LMap>
  </div>
</template>

<script>
import { LMap, LTileLayer, LMarker } from "vue2-leaflet";
export default {
  name: "Map",
  components: {
    LMap,
    LTileLayer,
    LMarker
  },
  data() {
    return {
      url: "https://{s}.tile.osm.org/{z}/{x}/{y}.png",
      zoom: 16,
      center: [13.1367826,77.5711133],
      bounds: null
    };
  },
  methods: {
    click (){
      console.log("click");
    },
    touch (){
      console.log("touch");
    }
  }
};
</script>
mikeu commented

Thanks for the example, it helps to see exactly what you're trying to do. As far as I can tell at the moment, the problem is simply that the functionality you're looking for isn't part of the Leaflet mapping library.

I'm still curious if you've investigated this at all in Leaflet itself. Here's a quick demo of roughly what you're attempting to do, in which I am not seeing any touchstart events on the markers: https://codesandbox.io/s/leaflet-touchstart-z9k6e

It's entirely possible I've missed something of course. Are you able to come up with a demo showing that what you're trying to do does work in vanilla Leaflet, but the same set of options and components doesn't work in Vue2Leaflet?

your're right,

the problem seems to be that leaflet decides internally which events are taken.

when i watch this demo:
https://leafletjs.com/examples/quick-start/
the following difference results on an iPad and a Windows 10 multi-touch screen.

If I keep one finger on the screen, whether on the map or outside, I can still touch on the iPad, but not on the desktop PC. the screen is blocked.

This must of course not be the case, because it is completely normal that the user of public information systems, e.g. puts one hand on the screen and touches with the other.

I try it in the leaflet forum.

one hacky solution could be to give the marke or tooltip a data attribute, then catch the touch event on the background of the map and react on it.

mikeu commented

OK, good to know! Hopefully they can help over at Leaflet. If they end up fixing the problem on their end and it still doesn't work in Vue2Leaflet with the updated version, then of course please open another issue here for that. In the meantime, I'll close this one.

hi, I've fixed the touch blocking inside the leaflet API, that's great now, I can have multiple maps open and the multiple users can touch and scroll. the leaflet cluster works great.

but for touch zoomen I tried to seperate the touch input for each map ID, the last map overwrites the settings each time. I brought a variable mapSearchID via options into the map, then the touches should only be registered for the map with the right id. but I think the var is overwritten each time a new map opens. leaflet registers the touchpoints globally on the document, which is for me a massive bug.

Is it possible to create multiple LMaps with own Leaflet instances?

somewhere in the leaflet API:

function _addPointerStart(obj, handler, id) {
  	var onDown = bind(function (e) {

  		if (e.MSPOINTER_TYPE_TOUCH && e.pointerType === e.MSPOINTER_TYPE_TOUCH) {
  			preventDefault(e);
  		}
  		_handlePointer(e, handler);
  	});

  	obj['_leaflet_touchstart' + id] = onDown;
  	obj.addEventListener(POINTER_DOWN, onDown, false); // console.log("000", mapSearchID)
  	// need to keep track of what pointers and how many are active to provide e.touches emulation
  	if (!_pointerDocListener) {
  	   // we listen document as any drags that end by moving the touch off the screen get fired there
  	   console.log(mapSearchID)
	   var mapEl = document.getElementById(mapSearchID) 
		
	    mapEl.addEventListener(POINTER_DOWN, _globalPointerDown, true); 
  	    mapEl.addEventListener(POINTER_MOVE, _globalPointerMove, true);
  	    mapEl.addEventListener(POINTER_UP, _globalPointerUp, true);
  	    mapEl.addEventListener(POINTER_CANCEL, _globalPointerUp, true);
  	    _pointerDocListener = true;
  	  }
  }
mikeu commented

Hi @bluelemonade , this still sounds to me to be an issue with Leaflet itself.

As far as I know there shouldn't be any problem creating multiple maps on a page from a single instance of Leaflet, or Vue2Leaflet. In fact, I just tested to be sure, and indeed you can simply create a second map, and at least for the simple example of click, each one gets its own unique event handler: https://codesandbox.io/s/two-maps-3v5f5. I am not sure about Microsoft-specific touch multi-touch events though.

there'es no problem with multiple maps, but I try to pass each map an mapsearchID, a unique ID of the map.

when I put this ID in a varibale inside the MAP with

function createMap(id, options) {
	  mapSearchID = options.mapSearchID;
	  // console.log("createMap", options)
  	  return new Map(id, options);
  }

I can check if the touchpoint is inside the map.   but when I open another map this map owerwrites my variable. I have no experiences with these factory setup. how can I read private properties inside options inside the API?

mikeu commented

That still really looks to me as if you are running into issues with Leaflet itself, not Vue2Leaflet. I'm not sure where your mapSearchID variable is defined, but if it is a variable defined once in the scope where that function gets called multiple times, then it makes sense that each call overwrites the previous value. Perhaps you could do something like this instead?

function createMap(id, options) {
  const map = new Map(id, options);
  map.searchId = options.mapSearchId;
  return map;
}

Either that or you could maybe define an object of mapSearchIds = {};, and then inside your existing createMap function replace mapSearchID = options.mapSearchID with mapSearchIds[id] = options.mapSearchID, to keep track of multiple different ids.

Alternatively, if you are trying to add custom options to the Leaflet Map class, then perhaps inheriting from L.Map and adding your new options would be a better way to go. Unfortunately this might make it a lot more complicated to keep working with the default Vue2Leaflet l-map component.

that's great, but how can I access the returned map inside the API, inside a function like function _addPointerStart(obj, handler, id)

mikeu commented

I'm sorry, I'm not too sure since that function is part of the internals of a separate, independent library, that we here at Vue2Leaflet are not associated with and have no control or influence over. My first guess would be that perhaps the obj argument will contain the map in the case that the event handler is being added to a map. Maybe you could do something like if (obj[searchId]) { newMapSearchFuntionality(obj); }.

The good news over here is that if you do get this new functionality accepted by Leaflet and they release a new version with your pull request merged into it, then it should simply start working with Vue2Leaflet as soon as you update your dependency, without any need for a new version to be released here as well.