如何防止默认处理触摸事件?

How to prevent default handling of touch events?

我正在尝试做类似于 embedded Google maps 所做的事情。我的组件应该忽略单点触摸(允许用户滚动页面)并在自身外部捏合(允许用户缩放页面),但应该对双点触摸做出反应(允许用户在组件内部导航)并且在这种情况下不允许任何默认操作。

如何防止触摸事件的默认处理,但仅限于用户用两根手指与我的组件交互的情况?

我尝试过的:

  1. 我尝试捕获 onTouchStartonTouchMoveonTouchEnd。事实证明,在 FF Android 上,在组件上捏合时触发的第一个事件是单击 onTouchStart,然后是两次触摸 onTouchStart,然后是 onTouchMove。但是在 onTouchMove 处理程序中调用 event.preventDefault()event.stopPropagation() 不会(总是)停止页面 zoom/scroll。在第一次调用 onTouchStart 时防止事件升级确实有帮助 - 不幸的是,那时我还不知道它是否会是多点触控,所以我不能使用这个。

  2. 第二种方法是在 document.body 上设置 touch-action: none。这适用于 Chrome Android,但如果我在所有元素(我的组件除外)上设置它,我只能让它与 Firefox Android 一起使用。因此,虽然这是可行的,但它似乎可能会产生不必要的副作用和性能问题。 编辑: 进一步的测试表明,只有在触摸开始之前设置了 CSS,这才适用于 Chrome。换句话说,如果我在检测到 2 个手指时注入 CSS 样式,那么 touch-action 将被忽略。所以这在 Chrome.

  3. 上没有用
  4. 我也试过在组件挂载上添加一个新的事件监听器:

    document.body.addEventListener("touchmove", ev => {
      ev.preventDefault();
      ev.stopImmediatePropagation();
    }, true);
    

    touchstart 也是如此)。这样做在 Firefox Android 中有效,但在 Chrome Android.

  5. 中不起作用

我运行没主意了。是否有一种可靠的跨浏览器方式来实现 Google 显然所做的,或者他们是否在每个浏览器上使用了多个 hack 和大量测试来使其工作?如果有人指出我的方法中的错误或提出新方法,我将不胜感激。

尝试使用 if 语句查看是否有多个触摸:

document.body.addEventListener("touchmove", ev => {
  if (ev.touches.length > 1) {
    ev.preventDefault();
    ev.stopImmediatePropagation();
  }
}, true);

TL;DR:我在注册事件处理程序时缺少 { passive: false }

我对 preventDefault() 和 Chrome 的问题是由于他们的 scrolling "intervention"(阅读:破坏网络 IE-style)。简而言之,由于可以更快地处理不调用 preventDefault() 的处理程序,因此向 addEventListener 添加了一个名为 passive 的新选项。如果设置为 true,则事件处理程序承诺不调用 preventDefault(如果调用,调用将被忽略)。 Chrome 然而决定更进一步,使 {passive: true} 默认(从版本 56 开始)。

解决方案是调用事件侦听器 passive 显式设置为 false:

window.addEventListener('touchmove', ev => {
  if (weShouldStopDefaultScrollAndZoom) {
    ev.preventDefault();
    ev.stopImmediatePropagation();
  };
}, { passive: false });

请注意,这会对性能产生负面影响。

作为旁注,我似乎误解了touch-action CSS,但是我仍然无法使用它,因为它需要在触摸序列开始之前设置。但如果不是这种情况,它可能性能更高,似乎 supported on all applicable platforms (Safari on Mac does not support it, but on iOS it does). This post 说得最好:

For your case you probably want to mark your text area (or whatever) 'touch-action: none' to disable scrolling/zooming without disabling all the other behaviors.

应该在组件上设置 CSS 属性,而不是像我那样在 document 上设置:

<div style="touch-action: none;">
  ... my component ...
</div>

在我的情况下,我仍然需要使用 passive 事件处理程序,但了解选项是很好的...希望它能帮助别人。

这是我的想法:

我用 divopacity: 0 来覆盖 mapz-index > 地图 z-index

而我会在covered div中进行检测。如果我在这个 covered div 中检测到 2 fingers 被触摸,我将 display: none 这个 div 允许用户可以在这个地图中使用 2 个手指。

否则,在 document touchEnd 我将使用 display: block 恢复此 covered div 以确保我们可以滚动。

在我的例子中,我已经用

解决了它
@HostListener('touchmove', ['$event'])
    public onTouch(event: any): void {
    event.stopPropagation();
    console.log('onTouch', event);
}