Android browser: touchcancel being fired althought

2020-04-02 08:03发布

问题:

I'm trying to build a Webpage that senses the touch from the user and drags and object along the canvas.

So I'm doing something like this:

var touchStart = function(e) {
    e.preventDefault();
    // Do stuff
}
var touchMove = function(e) {
    e.preventDefault();
    console.log("Touch move");
    // Move objs
}
var touchEnd = function(e) {
    e.preventDefault();
    console.log("Touch start!");
    // clean up stuff
}
var touchCancel = function(e) {
    e.preventDefault();

    // Oh NO touch cancel!
    console.log("Touch cancel!");

}
bindElemOrig.addEventListener('touchstart', touchStart, false);
bindElemOrig.addEventListener('touchmove', touchStart, false);
bindElemOrig.addEventListener('touchend', touchStart, false);
bindElemOrig.addEventListener('touchcancel', touchStart, false);

It works fine until some point.

The problem is that as soon as I load too many objs, it seems to me that the touchmove takes too long to respond, and touchcancel gets triggered. The problem is that as soon as touchcancel get triggered I don't receive any more touchmoves events, and I cannot sense the movement anymore.

Did anyone face this problem? I know about the bug in Android where you must call preventDefault (touchend event in ios webkit not firing?) but on this case it seems that it is not working because of the memory burden.

Thank you!

回答1:

like this

var touchMove = function(e) {
    e.preventDefault();
    setTimeout(function(){
        console.log("Touch move");
    // Move objs

    })
}

use setTimeout to wrap you logic in touchmove can solve this problem



回答2:

This problem may be due to a bug (feature?) in Chrome/Android. See this bug report.

This test demonstrates the behavior (JSFiddle):

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      var delay = 200;

      var haltEvent = function(event) {
        event.preventDefault();
        event.stopPropagation();
      };

      var pause = function() {
        var startTime = new Date().getTime();

        while (new Date().getTime() < startTime + delay);
      };

      window.addEventListener('load', function() {
        var target = document.querySelector('#target');
        var status = document.querySelector('#status');

        target.addEventListener('touchstart', function(event) {
          haltEvent(event);
          status.innerHTML = '[touchstart]';
        }, true);

        target.addEventListener('touchmove', function(event) {
          pause();
          haltEvent(event);
          status.innerHTML = '[touchmove]';
        }, true);

        target.addEventListener('touchend', function(event) {
          status.innerHTML = '[touchend]'; 
        }, true);

        target.addEventListener('touchcancel', function(event) {
          status.innerHTML = '[touchcancel]'; 
        }, true);
      });
    </script>
    <style>
      #target {
        background-color: green;
        height: 300px;
      }

      #status {
        text-align: center;
      }
    </style>
  </head>
  <body>
    <div id="target"></div>
    <p id="status">[]</p>
  </body>
</html>

I don't find the touchcancel event to be fired randomly. Instead, it is fired whenever it takes ~200 ms to return from a touchmove event handler.



回答3:

Touchcancel is specifically designed to take control of touch events and perform regular browser actions such as pan or zoom after a very small touch and move action (nearly 20px, depending on browser).

Your only options are to either:

  1. Switch to Pointer Events and use the touch-action CSS property as explained here to prevent pointercancel from firing (recommended but requires Android 5+)
  2. Add event.preventDefault() inside your touchmove event handler, but this will also disable any default actions like scroll, pan, zoom, etc.

You cannot attemp to cancel or preventDefault() inside a touchcancel function because it will be too late for that; the browser has already stopped listening to the touchmove event. This is by design, not a bug.