指明方向

Sérgio Gomes

指向网络上的事物曾经是轻而易举的。你有一个鼠标,你来移动它 有时候,你会按下按钮,就这样所有不是 鼠标被模拟成一个鼠标,开发者也清楚该看什么。

不过,“简单”不一定代表“良好”。但随着时间的推移 重要的是,并非所有东西都是(或假装)鼠标: 压力感应笔和倾斜度感知笔,可实现超凡的创作自由度;你可以 因此您只需使用设备和手即可;嘿,为什么 不用超过一根手指?

我们推出过触摸事件 但它们是一个完全独立的 API 否则,您必须编写两个单独的事件模型, 支持鼠标和触摸。Chrome 55 附带更新的标准版 统一了这两种模型的工具:指针事件

“单一事件”模型

指针事件可统一 浏览器的指针输入模型,将触摸、钢笔和鼠标结合在一起 事件。例如:

document.addEventListener('pointermove',
    ev => console.log('The pointer moved.'));
foo.addEventListener('pointerover',
    ev => console.log('The pointer is now over foo.'));

下面列出了所有可用的事件 您需要比较熟悉的鼠标事件:

pointerover 指针已进入元素的边界框。 如果设备支持悬停操作,此测试会立即生效, pointerdown 事件。
pointerenter pointerover 类似,但不以消息气泡和标识名显示 后代以不同的方式显示。 规范详情
pointerdown 指针已进入有效按钮状态,其中按钮处于未激活状态 按钮,具体取决于 输入设备。
pointermove 指针的位置发生了变化。
pointerup 指针已退出活动按钮状态。
pointercancel 发生了一些情况,这意味着指针不太可能发出任何 更多活动也就是说,您应该取消所有正在进行的操作 并将输入状态恢复到中立输入状态
pointerout 指针已离开元素或屏幕的边界框。此外,在 pointerup(如果设备不支持悬停)。
pointerleave pointerout 类似,但不以消息气泡和标识名显示 后代以不同的方式显示。 规范详情
gotpointercapture 元素收到了指针捕获
lostpointercapture 正在捕获的指针已被 发布。

不同的输入类型

通常,利用指针事件,你能够以与输入无关的方式编写代码, 而无需为不同的输入设备注册单独的事件处理脚本。 当然,您仍然需要注意各输入类型之间的差异,例如, 悬停的概念适用如果您确实想要区分不同的输入设备类型,例如可以提供 针对不同输入使用单独的代码/功能 - 但您可以从 事件处理脚本内使用pointerType PointerEvent 界面。例如,如果您为侧边抽屉式导航栏编写代码, 对 pointermove 事件具有以下逻辑:

switch(ev.pointerType) {
    case 'mouse':
    // Do nothing.
    break;
    case 'touch':
    // Allow drag gesture.
    break;
    case 'pen':
    // Also allow drag gesture.
    break;
    default:
    // Getting an empty string means the browser doesn't know
    // what device type it is. Let's assume mouse and do nothing.
    break;
}

默认操作

在支持触控功能的浏览器中,某些手势用于滚动、缩放或刷新网页。 对于触摸事件,当这些默认事件发生时, 是否发生了操作,例如,当用户滚动时,系统仍会触发 touchmove

对于指针事件,每当触发滚动或缩放等默认操作时, 您会收到一个 pointercancel 事件,告知您浏览器 可以对指针进行控制例如:

document.addEventListener('pointercancel',
    ev => console.log('Go home, the browser is in charge now.'));

内置速度:默认情况下,此模型可实现更好的效果。 相比触摸事件,您需要使用 被动事件监听器 以实现相同水平的响应速度。

您可以使用 touch-action CSS 属性。将某个元素的设置为 none 会停用所有 浏览器定义的操作针对相应元素开始。但还有一些 用于更精细控制的其他值(例如 pan-x),以允许 浏览器对沿 x 轴(而不是 y 轴)的移动做出反应。Chrome 55 支持以下值:

auto 默认;浏览器可以执行任何默认操作。
none 不允许浏览器执行任何默认操作。
pan-x 浏览器只能执行水平滚动默认操作。
pan-y 浏览器只能执行垂直滚动默认操作。
pan-left 浏览器只能执行水平滚动默认操作 并向左平移网页。
pan-right 浏览器只能执行水平滚动默认操作 并向右平移网页
pan-up 浏览器只能执行垂直滚动默认操作 并且仅向上平移网页
pan-down 浏览器只能执行垂直滚动默认操作 且仅向下平移网页
manipulation 浏览器只能执行滚动和缩放操作。

指针捕获

曾经花费令人沮丧的小时来调试损坏的 mouseup 事件,直到您意识到这是因为用户松开按钮 超出您的点击目标?不是?好吧,也许就只有我一个人。

然而,到目前为止,还没有一种很好的方法可以解决这个问题。没问题, 您可以在文档上设置 mouseup 处理程序,并在 来跟踪对象这不是最简洁的解决方案, 不过,尤其是在您构建网络组件并尽量确保一切正常时 隔离。

有了指针事件,我们给出了更好的解决方案:您可以捕获指针、 这样,在输入时一定会收到 pointerup 事件(或其他难以捉摸的事件), 朋友)。

const foo = document.querySelector('#foo');
foo.addEventListener('pointerdown', ev => {
    console.log('Button down, capturing!');
    // Every pointer has an ID, which you can read from the event.
    foo.setPointerCapture(ev.pointerId);
});

foo.addEventListener('pointerup', 
    ev => console.log('Button up. Every time!'));

浏览器支持

在撰写本文时,Internet Explorer 11 支持指针事件, Microsoft Edge、Chrome 和 Opera;Firefox 部分支持。您可以 在 caniuse.com 上找到最新列表

您可以使用指针事件 polyfill 来实现 填补差距。或者,在运行时检查浏览器支持 简单明了:

if (window.PointerEvent) {
    // Yay, we can use pointer events!
} else {
    // Back to mouse and touch events, I guess.
}

指针事件非常适合进行渐进式增强: 修改初始化方法以进行上述检查,添加指针事件 if 程序块中的处理脚本,并将鼠标/触摸事件处理脚本移动到 else 代码块。

快来试用一下吧,并将您的想法告诉我们!