Firefox Nightly 中现已支持指针事件

[重要更新:本文发布后,由于稳定性错误,Firefox Nightly 中已禁用指针事件。此错误修复后,将重新启用指针事件。您仍然可以通过在 about:config 中将 dom.w3c_pointer_events.enabled 设置为“true”来在 Firefox 中测试指针事件。]

今年 2 月,指针事件 成为 W3C 推荐标准。在此期间,Microsoft Open TechMozilla 一直在合作实施该规范。随着消费者不断扩展用于探索 Web 的设备范围,并使用触摸、笔或鼠标等不同的输入机制,提供一个统一的 API 供开发人员在其应用程序中使用变得至关重要。在这项工作中,我们刚刚取得了一个重要的里程碑:指针事件现已在 Firefox Nightly 中可用。我们对这项工作感到非常兴奋,它代表了多个浏览器供应商之间的大量合作,旨在产出一个高质量的行业标准 API,并获得越来越广泛的支持。

请务必下载 Firefox Nightly 并试用,并通过 dev-platform 邮件列表或 mozilla.dev.platform 组向我们提供有关实现的反馈。如果您对规范有任何反馈,请发送至 public-pointer-events@w3.org。

此规范的目的是扩展开放 Web 以支持除鼠标之外的各种输入机制,同时保持与大多数基于 Web 的内容的兼容性,这些内容是围绕 鼠标事件 构建的。该 API 旨在创建一个可以处理各种输入设备的解决方案,重点是点设备(鼠标、笔和触摸)。规范中将指针定义为一种与硬件无关的设备,可以定位特定的一组屏幕坐标。指针事件在设计上与当前与鼠标事件关联的事件集相似。

在当前的 Nightly 版本中,现已支持鼠标输入的指针事件。此外,如果您使用的是 Windows,则设置两个首选项后,现在可以启用触摸事件。第一个属性,异步平移和缩放 (APZ),通过将 layers.async-pan-zoom.enabled Firefox 配置首选项 设置为 true 来启用。还应通过将此值设置为 1 来启用 dom.w3c_touch_events.enabled 首选项。

这篇文章介绍了新 API 的一些基本功能。

使用指针 API

在开始使用指针 API 之前,务必测试当前浏览器是否支持该 API。这可以通过类似于以下示例的代码来完成

if (window.PointerEvent) {
  .....
}else{
  // use mouse events
}

指针 API 提供了对 pointerdown, pointerup, pointercancel, pointermove, pointerover, pointerout, gotpointercapture,lostpointercapture 事件的支持。如果您以前编写过鼠标输入的事件处理代码,那么大多数这些事件应该都很熟悉。例如,如果您需要一个 Web 应用程序在触摸或单击画布时移动图像,则可以使用以下代码

function DragImage() {
    var imageGrabbed = false;
    var ctx;
    var cnv;
    var myImage;
    var x = 0;
    var y = 0;
    var rect;
    this.imgMoveEvent = function(evt) {
        if (imageGrabbed) {
            ctx.clearRect(0, 0, cnv.width, cnv.height);
            x = evt.clientX - rect.left;
            y = evt.clientY - rect.top;
            ctx.drawImage(myImage, x, y, 30, 30);

        }
    }
    this.imgDownEvent = function(evt) {
        //Could use canvas hit regions
        var xcl = evt.clientX - rect.left;
        var ycl = evt.clientY - rect.top;
        if (xcl > x && xcl < x + 30 && ycl > y && ycl < y + 30) {
            imageGrabbed = true;
        }
    }
    this.imgUpEvent = function(evt) {
        imageGrabbed = false;
    }
    this.initDragExample = function() {
        if (window.PointerEvent) {
            cnv = document.getElementById("myCanvas");
            ctx = cnv.getContext('2d');
            rect = cnv.getBoundingClientRect();
            x = 0;
            y = 0;
            myImage = new Image();
            myImage.onload = function() {
                ctx.drawImage(myImage, 0, 0, 30, 30);
            };
            myImage.src = 'images/ff.jpg';
            cnv.addEventListener("pointermove", this.imgMoveEvent, false);
            cnv.addEventListener("pointerdown", this.imgDownEvent, false);
            cnv.addEventListener("pointerup", this.imgUpEvent, false);
        }
    }
}

PointerCapture 事件用于指针设备可能在跟踪事件时离开现有元素区域的情况。例如,假设您正在使用滑块,并且您的手指从实际元素上滑落 - 您需要继续跟踪指针移动。您可以使用类似于以下的代码设置 PointerCapture

var myElement = document.getElementById("myelement");
myelement.addEventListener("pointerdown", function(e) {
    if (this.setPointerCapture) {
    //specify the id of the point to capture
        this.setPointerCapture(e.pointerId);
    }
}, false);

此代码保证您仍然会收到 pointermove 事件,即使您离开 myelement 的区域也是如此。如果您没有设置 PointerCapture,则当您的指针离开其区域时,包含元素将不会调用指针移动事件。您还可以通过调用 releasePointerCapture 来释放捕获。当 pointeruppointercancel 事件发生时,浏览器会自动执行此操作。

指针事件接口

PointerEvent 接口扩展了 MouseEvent 接口,并提供了一些其他属性。这些属性包括 pointerId, width, height, pressure, tiltX, tiltY, pointerTypeisPrimary

pointerId 属性为启动事件的指针提供了唯一的 ID。heightwidth 属性分别以 CSS 像素为单位提供了指针接触几何形状的值。当指针碰巧是鼠标时,这些值将设置为 0。pressure 属性包含一个从 0 到 1 的浮点值,以指示指针施加的压力量,其中 0 为最低,1 为最高。对于不支持压力的指针,该值将设置为 0.5。

tiltY 属性包含指针的 X-Z 平面与屏幕之间的角度值,范围在 -90 到 90 度之间。此属性在使用触控笔进行指针操作时最有用。0 度的值表示指针以相对于 Y 轴的精确垂直角度接触表面。同样,tiltX 属性包含 Y-Z 平面之间的角度。

pointType 属性包含指针表示的设备类型。当前,此值将设置为 mouse, touch, pen, unknown 或空字符串。

var myElement = document.getElementById("myelement");
myElement.addEventListener("pointerdown", function(e) {
    switch(e.pointerType) {
        case "mouse":
            console.log("Mouse Pointer");
            break;
        case "pen":
            console.log("Pen Pointer");
            break;
        case "touch":
            console.log("Touch Pointer");
            break;
        default:
            console.log("Unknown Pointer");
    }
}, false);

isPrimary 属性为 truefalse,指示指针是否为主指针。当支持多个触摸点以提供多点触控输入和手势支持时,需要主指针属性。当前,当指针首次与跟踪指针事件的元素接触时,此属性将为每种特定指针类型(鼠标、触摸、笔)设置为 true。如果您同时使用一个触摸点和一个鼠标指针,则两者都将设置为 true。如果另一个指针已使用相同的 pointerType 处于活动状态,则事件的 isPrimary 属性将设置为 false

var myElement = document.getElementById("myelement");
myelement.addEventListener("pointerdown", function(e) {
    if( e.pointerType == "touch" ){
         if( e.isPrimary ){
             //first touch
         }else{
             //handle multi-touch
         }
    }

}, false);

处理多点触控

如前所述,触摸指针目前仅在运行 Windows 的 Firefox Nightly 上实现,并且启用了 layers.async-pan-zoom.enableddom.w3c_touch_events.enabled 首选项。您可以使用以下代码检查是否支持多点触控。

if( window.maxTouchPoints && window.maxTouchPoints > 1 ){
//supports multi-touch
}

某些浏览器为某些触摸交互提供了默认功能,例如使用滑动手势滚动或使用捏合手势进行缩放控制。当使用这些默认操作时,不会触发指针的事件。为了更好地支持不同的应用程序,Firefox Nightly 支持 CSS 属性 touch-action。此属性可以设置为 auto, none, pan-x, pan-ymanipulation。将此属性设置为 auto 不会在使用触摸事件时更改浏览器的任何默认行为。要禁用所有默认行为并允许您的内容改为使用指针事件处理所有触摸输入,您可以将此值设置为 none。将此值设置为 pan-xpan-y 会在未在给定方向上平移/滚动时调用所有指针事件。例如,pan-x 在未在水平方向上平移/滚动时会调用指针事件处理程序。当属性设置为 manipulation 时,如果未发生平移/滚动或缩放操作,则会触发指针事件。

此元素在未在水平方向上平移时接收指针事件。
// Very Simplistic pinch detector with little error detection,
// using only x coordinates of a pointer event

// Currently active pointers
var myPointers = [];
var lastDif = -1;

function myPointerDown(evt) {
    myPointers.push(evt);
    this.setPointerCapture(evt.pointerId);
    console.log("current pointers down = " + myPointers.length);
}

//remove touch point from array when touch is released
function myPointerUp(evt) {
    // Remove pointer from array
    for (var i = 0; i < myPointers.length; i++) {
        if (myPointers[i].pointerId == evt.pointerId) {
            myPointers.splice(i, 1);
            break;
        }
    }
    console.log("current pointers down = " + myPointers.length);

    if (myPointers.length < 2) {
        lastDif = -1;
    }
}

//check for a pinch using only the first two touchpoints
function myPointerMove(evt) {
    // Update pointer position.
    for (var i = 0; i < myPointers.length; i++) { if (evt.pointerId = myPointers[i].pointerId) { myPointers[i] = evt; break; } } if (myPointers.length >= 2) {
        // Detect pinch gesture.
        var curDif = Math.abs(myPointers[0].clientX - myPointers[1].clientX);
        if (lastDif > 0) {
            if (curDif > lastDif) { console.log("Zoom in"); }
            if (curDif < lastDif) { console.log("Zoom out"); }
        }
        lastDif = curDif;
    }
}

您可以 在此处测试示例代码。有关指针事件 API 在实际应用中的一些出色示例,请参阅 Patrick H. Lauke 在 GitHub 上收集的 触摸和指针事件实验。Patrick 是 W3C 指针事件工作组、W3C 触摸事件社区组的成员,以及 Paciello Group 的高级无障碍顾问。

结论

在这篇文章中,我们介绍了目前在 Firefox Nightly 中实现的一些基础知识。要跟踪此 API 的进度,请查看 Gecko 触摸 Wiki 页面。您还可以关注主要 功能错误,并确保在测试新的指针 API 时报告您发现的任何问题。

关于 Matt Brubeck

Matt Brubeck 的更多文章……


17 条评论

  1. Patrick H. Lauke

    很棒的东西!一个小小的额外美味小点心:此版本使 Firefox Nightly 成为第一个支持指针事件的 OS X 浏览器(尽管是的,Chrome Canary 可以设置为通过 –enable-blink-features=PointerEvent 部分支持指针事件,但这目前仅在一定程度上适用于触摸交互)。

    2015年8月4日 09:55

  2. Patrick H. Lauke

    “在当前的 Nightly 版本中,现已支持鼠标输入的指针事件。此外,如果您使用的是 Windows,则设置两个首选项后,现在可以启用触摸事件。”

    我刚在我的 Surface 3 上安装了 Nightly,它似乎不仅仅是鼠标输入……触摸和触控笔也正确地触发了指针事件(分别具有相应的“touch”和“pen”指针类型)。因此,第一部分似乎不准确(变得更好了)。

    我假设“此外……”指的是实际的触摸事件(touchstart、touchmove、touchend)。这令人困惑,因为在第一次阅读时,我认为整个句子意味着“只有鼠标触发指针事件,但如果您启用触摸事件,您也会从触摸触发指针事件”。

    2015年8月4日 12:12

  3. Patrick H. Lauke

    ……尽管我现在看到触摸和触控笔的指针事件仅得到非常肤浅的支持。尽管如此,这仍然是小小的进步 :)

    2015年8月4日 12:19

  4. Felix

    在最后一段代码中,我认为您的意思是 lastDif = -1;

    2015年8月4日 15:18

    1. Matt Brubeck

      已修复,谢谢。

      2015年8月4日 15:55

  5. Pablo

    它是否适用于所有平台(Windows 7/8/10、Android……)?
    它是否解决了在浏览器中使用 Wacom 手写笔时的延迟问题?

    2015年8月5日 01:23

    1. Matt Brubeck

      目前,这仅在桌面平台(Windows、Mac、Linux)上启用。我们还需要做一些工作才能完成移动平台(Android、Firefox OS)的指针事件实现。

      我不确定这是否会影响 Wacom 手写笔的延迟。如果您想在 Nightly 中测试并告知我们,那将非常棒!

      2015年8月5日 16:41

      1. Pablo

        我在 Windows 8.1/10 上使用 Nightly 进行了测试,在使用带有集成 Wacom 数字转换器的平板电脑(Samsung Ativ 3)时仍然存在延迟。

        在 IE11/Edge 上没有延迟。

        您是否监听 OS 手写笔事件,或者您是否等待 OS 触发鼠标事件,然后才检查事件属性以发现它最初是手写笔事件?

        2015年8月16日 07:16

  6. Fred

    几天前,我了解到可能可以使用新的电池状态 API 来侵犯浏览器用户的隐私。[1]

    此新 API 是否存在类似的安全漏洞?

    您在安全方面的朋友,
    Fred

    [1] http://it.slashdot.org/story/15/08/03/1728255/privacy-alert-your-laptop-or-phone-battery-could-track-you-online

    2015年8月5日 上午05:14

    1. Matt Brubeck

      指针事件 API 主要只是为网页通过其他 API(鼠标和触摸输入)已经可以访问的相同数据提供了一种更一致的格式。规范的这部分没有为网页提供任何新的跟踪或识别用户的方式。

      规范确实定义了一个新的“navigator.maxTouchPoints”属性,允许网页查看客户端触摸屏可以一次检测到的手指的大致数量。这提供了一些关于用户硬件的信息,但这些信息不是非常独特(您的设备很可能与许多其他设备具有相同的 maxTouchPoints),因此对于跟踪或指纹识别来说,其用处很小。

      2015年8月5日 下午16:38

  7. 代码被双重 HTML 编码了!

    代码被双重 HTML 编码了!

    它实际上显示“& ” & “>” 而不是“&” & “>”。

    以防万一此评论也被编码或双重编码,我将拼写出我刚才说的话……

    它实际上显示“ampersand-a-m-p-semicolon” & “ampersand-g-t-semicolon” 而不是“ampersand” & “greater-than-symbol”。

    2015年8月5日 上午08:48

    1. Dan Callahan

      糟糕!正在修复它。感谢您的提醒 :)

      2015年8月5日 上午10:17

  8. ecloud

    我们什么时候可以期望它在 Linux 上运行?(即,使用 XInput 2.2)我发现了这个错误,它链接到其他错误:https://bugzilla.mozilla.org/show_bug.cgi?id=711711,但最近没有太大进展?

    2015年8月6日 上午05:19

    1. Matt Brubeck

      图形和平台团队正在积极处理阻止该错误的两个主要问题(升级到 GTK3 并启用异步平移/缩放),但我不知道他们何时预计准备好发布,抱歉。

      2015年8月6日 上午10:43

  9. George

    我们可以在下一个 Firefox nightly 版本中期待 Pointer Events 吗?

    2015年8月8日 上午00:26

    1. Matt Brubeck

      Pointer Events 已经在 Firefox 的 Windows、Mac 和 Linux nightly 版本中启用。

      2015年8月8日 上午10:28

  10. Neville

    Matt 做得好……期待在 Firefox 中看到最终结果。

    2015年8月9日 下午15:29

本文的评论已关闭。