hammerjs / hammer.js

A javascript library for multi-touch gestures :// You can touch this
http://hammerjs.github.io
MIT License
24.1k stars 2.63k forks source link

Double event issue with hybrid mouse/touch system #447

Closed moeriki closed 9 years ago

moeriki commented 10 years ago

Dear all,

I'm writing software for a multitouch board. I have an issue with double events.

The demo below is tested on Chrome 32, Firefox 26 and Safari 7.0.1 on OS X.

I have the following log line in my main file.

Hammer(document).on('tap doubletap', function(e) {
  console.log('Hammer ' + e.type);
});

I put the following log (in hammer.js) right before checking all the gestures.

console.log('check all gestures for ' + eventData.srcEvent.type + '/' + eventData.eventType);
Hammer.utils.each(this.gestures, function(gesture) {
  // …
});

This is my console output on a single tap.

check all gestures for touchstart/start (hammer.js:833)
check all gestures for touchend/end (hammer.js:833)
Hammer tap
check all gestures for mousedown/start (hammer.js:833)
check all gestures for mouseup/end (hammer.js:833)
Hammer doubletap
Hammer tap

As you can obviously see my system triggers mouse events (for the first touch), as well as touch events for all touches.

Has anyone had this problem, and is there a known solution for this? As for now I'll continue debugging and trying out solutions.

Sidenote: I can't disable mouse events. The board I develop for is connected to a desktop computer. Thus the application should be usable via mouse on desktop, as well as via touch on the board.

Thanks for reading.

moeriki commented 10 years ago

If no known solutions are available, my first suggestion would be that I write an option _ignore_firsttouch which would ignore the first touch for single touch gestures (tap, doubletap, hold, etc…). I'm not sure how, but I'm looking into it.

Would that be something to accept into the repository, or would you consider it overhead?

moeriki commented 10 years ago

My solution so far.

There's a boolean _touchtriggered which is set to true when handling a touch event. At the end of onTouch this is set to false again. I added a timeout of 10ms. I wanted to make this a configurable option, but I can't seem to find a good way to access the instance options in the onTouch event handler.

var touch_triggered = false;
var touch_triggered_timeout;
if(enable_detect) {
  // …
  // touch
  else if(sourceEventType.match(/touch/)) {
    count_touches = ev.touches.length;
    window.clearTimeout(touch_triggered_timeout);
  }
  // …
}
if(!count_touches) {
  last_move_event = null;
  enable_detect = false;
  touch_triggered_timeout = window.setTimeout(function() {
    touch_triggered = false;
  }, 10);
  Hammer.PointerEvent.reset();
}

This is just to give you a gist of the way it works for me. Would something along these lines be acceptable as a pull request?

ergousha commented 10 years ago

Can you try stopImmediatePropagation?

Hammer($this).on("tap", function(event) { event.stopImmediatePropagation(); }

moeriki commented 10 years ago

Just tried it, still double event. tap/doubletap/tap on a single touch

claytongulick commented 10 years ago

This is sort of lame, but here's how I solved it:

 $("#some_element").hammer().on("tap",
   function(evt)
   {
     debounce(
       function()
       {             
         console.log(evt);
       }
     );
   });

var debounce_count=0;
function debounce(callback)
{
  debounce_count++;
  setTimeout(function()
    {
      debounce_count--;
      if(debounce_count == 0) callback();
    },10);
}
claytongulick commented 10 years ago

Also, found a simpler solution.

$("#some_element").hammer().on("tap",
    function(evt)
    {
        evt.gesture.srcEvent.preventDefault();
    });

This fixed the issue for me.

normanrz commented 10 years ago

I'm having the same problem. My solution was to override the emit function and only allow touch events:

    Hammer.Manager.prototype.emit = ->
      return (type, data) ->
        return if data.pointerType == "mouse"
        type = "hammer-input" if type == "hammer.input"
        $(this.element).trigger(
          type: type
          gesture: data
          target: data.target
        )

I only need touch events because I'm making a Phonegap app. I also overrode the function to get jQuery events (could also work with domEvents option).

Maybe there is a better solution?

emin93 commented 9 years ago

Same problem here, getting the event twice on a single tap.

arschmitz commented 9 years ago

Closing this a duplicate of #292