Skip to main content

为什么你的处理程序在移动设备上触发两次,以及拖动后的 Click

关于画布上的指针输入,有两件事经常让人感到意外。一件是真正需要处理的问题,另一件已经由系统处理好了,了解这一点可以避免使用不必要的变通方案

一次点击可能触发三个事件

Konva 会监听全部三种 DOM 输入系列——鼠标、触摸和指针——这样处理程序无需提前选择,就能在任何设备上工作。它不会将这些事件合并

在触摸屏上,浏览器会发送一对 touchstart/touchend,一对 pointerdown/pointerup,然后为了兼容触摸出现之前编写的页面,合成 mousedown/mouseup。Konva 会将其中每一对都转换为自己的事件。一次点击会产生:

Konva 事件来源
tap触摸事件对
pointerclick指针事件对
click合成的鼠标事件对

因此,这段代码在手机上会运行你的处理程序两次,在桌面设备上运行一次:

// Fires for `click` and again for `tap` on a touch device.
shape.on('click tap', handleSelect);

绑定一个系列

最简单的修复方法是停止配对事件,改用指针系列,它已经覆盖鼠标、触摸和触控笔:

shape.on('pointerclick', handleSelect);   // once, on every device

pointerdownpointermovepointerup 的工作方式相同。指针事件默认启用(Konva.pointerEventsEnabled),更多信息请参阅 Pointer Events

如果你需要支持不提供指针事件的浏览器,或者正在维护已经配对 click tap 的代码,也可以改为保护处理程序:

let lastHandled = 0;

function handleSelect(e) {
// A synthesised mouse event follows its touch within about 300ms.
const now = Date.now();
if (now - lastHandled < 400) return;
lastHandled = now;

// ...
}

优先绑定一个系列。计时器只是一个变通方案,并且会吞掉快速用户发起的真正第二次点击

拖动后 Click 本来就不会触发

这一点值得了解,因为通常的变通方案并没有必要

当拖动真正开始时,Konva 会清除允许分发 click 的标志,因此 dragend 不会紧接着触发 click

shape.on('dragend', () => console.log('dragged'));
shape.on('click', () => console.log('clicked'));

// Drag the shape: you get 'dragged' only.
// Press and release without moving: you get 'clicked' only.

如果你无论如何都看到了拖动后的 click,通常是以下几种情况之一

你监听的是 mouseup,而不是 click mouseup 是原始指针事件,无论是否拖动都会触发。Konva 抑制的是 click

指针移动的距离不够远,无法被算作拖动。 Konva.dragDistance 默认为 3 像素。一次按下后移动一两像素——这在触摸屏上很正常——会被视为 click,并且 dragstart 也不会触发。这是预期行为。如果用户在小目标上操作时手部容易抖动,可以增大该值:

Konva.dragDistance = 8;

你将 dragDistance 设置为了 0 这样每次按下都会被视为拖动,从而抑制你想要的 click

防止浏览器自身的手势

与 Konva 无关的是,画布上的触摸可能会滚动页面或触发双击缩放。这是浏览器的行为,可以通过 CSS 停止:

#container {
touch-action: none;
}

如果页面仍应支持垂直滚动,而画布处理水平拖动,请改用 touch-action: pan-y。完整说明请参阅 Mobile Scrolling

相关内容