使用 W3C PointerEvent API 并能够检测到双击?

Using W3C PointerEvent API and be able to detect a double click?

提问人:Chris 提问时间:4/21/2023 最后编辑:Chris 更新时间:5/14/2023 访问量:560

问:

编写一个丰富的 Web 应用程序,我需要支持所有类型的用户,有些使用鼠标,有些使用触摸屏(例如在移动设备上)。因此,W3C PointerEvent API 正是我处理用户交互所需要的。这很好用,除了一个重要情况:双击。

有几件事阻碍了:

  • 没有像“”这样的(高级)事件,只有低级和可用。pointerdblclickpointerdownpointerup
  • 要解决此问题并自己计算双击,通常建议使用发出事件的属性并检查它是否具有值。但这个属性总是在我的情况下。detail20
  • 但是,即使我自己跟踪这个数字也无法完美地工作,因为操作系统(以及用户可能的设置)正在定义最大延迟,即双击两次。

那么,当所有其他用户交互都是通过侦听 PointerEvents 来处理时,我该如何对鼠标双击或触摸双击做出反应呢?甚至可以以尊重双击延迟的操作系统设置的方式做到这一点吗?

注 1:我仍然必须能够检测到正常的点击(通过听 和 )以及拖拽(通过听 和 稍后)
注 2:我使用的是“纯”JavaScript 和现代浏览器,jQuery 是没有选择的。
pointerdownpointeruppointerdownpointermovepointerup

javascript 双击 pointer-events

评论

0赞 evolutionxbox 4/21/2023
这有帮助吗?developer.mozilla.org/en-US/docs/Web/API/Element/dblclick_event
0赞 Chris 4/21/2023
可悲的是:不,它没有。作为我的听众,并且正在阻止它被发射。pointerdownpointerup
0赞 amitp 5/13/2023
我相信并且是高级事件,而mousedown/mouseup和pointerdown/pointerup是低级事件。浏览器应该同时向您发送低级和高级事件,并且您可以对 做出反应。我的猜测是,棘手的事情是弄清楚是否取消(preventDefault)某些事件。clickdblclickdblclick

答:

1赞 amitp 5/14/2023 #1

下面是一个可运行的示例,用于侦听用于拖动的低级指针事件高级单击事件。当按下鼠标按钮、移动鼠标和释放按钮时,我们将收到一个事件。但我们可能想把它当作一种拖累。所以我有一个变量来检测是否有拖累。clickgotMoveEvent

// code based on https://www.redblobgames.com/making-of/draggable/

const messages = document.querySelector("figcaption");
const draggable = document.querySelector("circle");

let dragging = false;
let gotMoveEvent = false;

draggable.onpointerdown = (event) => {
   if (event.button !== 0) return; // only drag on left click
   draggable.classList.add("dragging"); // css feedback
   event.currentTarget.setPointerCapture(event.pointerId); // capture
   dragging = true;
   gotMoveEvent = false;
   messages.innerText = "Click or drag?";
}

draggable.onpointerup = (event) => {
   dragging = false;
   draggable.classList.remove("dragging"); // css feedback
}

draggable.onpointercancel = draggable.onpointerup;

draggable.onpointermove = (event) => {
   if (!dragging) return;
   gotMoveEvent = true;
   messages.innerText = "Dragging";
   let p = convertPixelToSvgCoord(event);
   draggable.setAttribute("transform", `translate(${p.x},${p.y})`);
}

draggable.onclick = (event) => {
   if (gotMoveEvent) {
      messages.innerText = "Ignoring click event because we were dragging";
      event.preventDefault();
      return;
   }
   messages.innerText = "Got click";
}

draggable.ondblclick = (event) => {
   // maybe: check for gotMoveEvent here too?
   messages.innerText = "Got dblclick";
}



/** Convert from event coordinate space (on the page) to SVG coordinate
 * space (within the svg, honoring responsive resizing, width/height,
 * and viewBox) */
function convertPixelToSvgCoord(event, relativeTo=event.currentTarget.ownerSVGElement) {
    // if relativeTo is the <svg> then its ownerSVGElement is null, so we want to point back to the <svg>
    // but otherwise we assume it's a child of <svg> and we want to find the <svg>
    let p = (relativeTo.ownerSVGElement ?? relativeTo).createSVGPoint();
    p.x = event.clientX;
    p.y = event.clientY;
    return p.matrixTransform(relativeTo.getScreenCTM().inverse());
}
.draggable { fill: hsl(0 50% 50%); cursor: grab; }
.draggable.dragging { fill: hsl(200 50% 50%); cursor: grabbing; user-select: none; }
<figure>
<figcaption>Drag or click</figcaption>
<svg viewBox="-200 -50 400 100" style="background:hsl(60 5% 95%)">
  <circle class="draggable" r="20" />
</svg>
</figure>