使用 Pointer Events

Baseline 广泛可用 *

此特性已经十分成熟,可在许多设备和浏览器版本上使用。自 2020 年 7 月以来,它已在各大浏览器中可用。

* 此特性的某些部分可能存在不同级别的支持。

本指南演示如何使用 pointer events 和 HTML <canvas> 元素构建支持多点触控的绘图应用程序。此示例基于 touch events overview 中的示例,但它使用了 pointer events 输入事件模型。另一个不同之处在于,由于 pointer events 是设备无关的,因此该应用程序使用相同的代码接受来自鼠标、笔或指尖的基于坐标的输入。

此应用程序仅在支持 pointer events 的浏览器中运行。

定义

Surface

一个支持触摸的表面。这可能是一个触摸板、触摸屏,甚至是用户桌面表面(或鼠标垫)与物理屏幕的虚拟映射。

Touch point

与表面的接触点。这可能是一个手指(或肘部、耳朵、鼻子,任何东西,但通常是手指)、手写笔、鼠标,或任何其他用于在表面上指定单个点的方法。

示例

注意: 下面的文本在描述与表面的接触时使用了“手指”一词,但当然也可以是手写笔、鼠标或其他指向某个位置的方法。

Drawing application

HTML

HTML 由一个单独的 <canvas> 元素组成。曲线将根据用户的触摸手势绘制。还包含一个用于清除画布的按钮。

html
<canvas id="canvas" width="600" height="600">
  Your browser does not support the canvas element.
</canvas>
<button id="clear">Clear canvas</button>

CSS

设置了 touch-action 属性为 none,以防止浏览器对应用程序应用其默认的触摸行为。

css
#canvas {
  border: solid black 1px;
  touch-action: none;
  display: block;
}

JavaScript

我们将跟踪所有正在进行的触摸,并为每个触摸绘制线条。colors 用于区分不同的手指。

js
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");

// Mapping from the pointerId to the current finger position
const ongoingTouches = new Map();
const colors = ["red", "green", "blue"];

handleStart 函数监听 pointerdown 事件,并在触摸开始时绘制一个圆圈。

js
function handleStart(event) {
  const touch = {
    pageX: event.pageX,
    pageY: event.pageY,
    color: colors[ongoingTouches.size % colors.length],
  };
  ongoingTouches.set(event.pointerId, touch);

  ctx.beginPath();
  ctx.arc(touch.pageX, touch.pageY, 4, 0, 2 * Math.PI, false);
  ctx.fillStyle = touch.color;
  ctx.fill();
}

canvas.addEventListener("pointerdown", handleStart);

handleEnd 函数监听 pointerup 事件,并在触摸结束时绘制一个正方形。

js
function handleEnd(event) {
  const touch = ongoingTouches.get(event.pointerId);

  if (!touch) {
    console.error(`End: Could not find touch ${event.pointerId}`);
    return;
  }

  ctx.lineWidth = 4;
  ctx.fillStyle = touch.color;
  ctx.beginPath();
  ctx.moveTo(touch.pageX, touch.pageY);
  ctx.lineTo(event.pageX, event.pageY);
  ctx.fillRect(event.pageX - 4, event.pageY - 4, 8, 8);
  ongoingTouches.delete(event.pointerId);
}

canvas.addEventListener("pointerup", handleEnd);

handleCancel 函数监听 pointercancel 事件,并停止跟踪触摸。

js
function handleCancel(event) {
  const touch = ongoingTouches.get(event.pointerId);

  if (!touch) {
    console.error(`Cancel: Could not find touch ${event.pointerId}`);
    return;
  }

  ongoingTouches.delete(event.pointerId);
}

canvas.addEventListener("pointercancel", handleCancel);

handleMove 函数监听 pointermove 事件,并在触摸的开始和结束之间绘制一条线。

js
function handleMove(event) {
  const touch = ongoingTouches.get(event.pointerId);

  // Event was not started
  if (!touch) {
    return;
  }

  ctx.beginPath();
  ctx.moveTo(touch.pageX, touch.pageY);
  ctx.lineTo(event.pageX, event.pageY);
  ctx.lineWidth = 4;
  ctx.strokeStyle = touch.color;
  ctx.stroke();

  const newTouch = {
    pageX: event.pageX,
    pageY: event.pageY,
    color: touch.color,
  };

  ongoingTouches.set(event.pointerId, newTouch);
}

canvas.addEventListener("pointermove", handleMove);

最后,添加清除功能。

js
document.getElementById("clear").addEventListener("click", () => {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
});

规范

规范
指针事件
# pointerevent-interface

浏览器兼容性

另见