javascript - 如何使用指针事件覆盖 firefox 和 chrome 中的触摸缩放和平移

标签 javascript firefox

这也适用于其他浏览器,但主要目标是 Firefox 和 Chrome。

我已经注册了所有指针事件监听器并且必须注册鼠标按下和鼠标向上监听器来获取按钮状态变化(指针事件中的按钮状态不准确或“和弦”)

但即使我对所有指针执行 event.preventDefault():over、enter、down、up、out 和 leave 事件(pointerType mouse for down 除外,因为它会禁用 mousedown 和 mouse up 监听器)我还有其他问题:

多点触控(多于一根手指)将被接管并执行页面缩放操作,使我的应用中的多点触控变得无用。

如果页面上有滚动条,触摸平移/滚动操作将接管单次触摸。

preventDefault() 未在操作系统(即:windows 10)中正确实现,在我的元素上向下的指针也会生成合成鼠标按下并将鼠标合成移动到触摸位置。我可以用困惑的代码忽略合成的向下和向上事件)但它会将鼠标放在意想不到的位置,并防止在需要时同时使用鼠标和触摸作为单独的输入。 (解决这个问题可能需要在 DOM 元素本身而不是每个事件的基础上设置一个标志,因为内部事件队列将在操作系统和浏览器调度的事件之间放置延迟 - 我已经在我的 native 应用程序中解决了这个问题)

我正在使用 Web 开发人员控制台进行测试,因此我可以记录状态 - 我不知道这是否会影响事情。

我在 Chrome 上也有同样的触摸平移/缩放缺乏控制。 Chrome 和 Firefox 上的鼠标上/下行为明显不同,因此需要单独的代码路径。 对战

对于代码迷——注意这是一个接口(interface)设备“前端” “webasm”(游戏或生活游戏)引擎:)

目标是为任何进入 Canvas 元素的指针(未被另一个元素的处理程序捕获)获取“干净的原始”事件流。使用捕获的指针镜像所有受支持设备上所有按钮的状态,直到捕获丢失。还能够将鼠标和触摸(以及任何其他支持的设备)用作单独的设备,忽略所有合成事件并希望关闭我正在收听的一个设备被我正在收听的另一个设备模拟的任何东西(所以他们不'战斗)。还可以完全控制对事件执行的操作,直到丢失指针的事件流捕获为止。我想捕获在触摸设备(平板电脑、面板、屏幕等)的元素上发生“主要”触摸之后和同时发生的所有触摸,如果不使用我们的 UI,则可以选择让它们“掉落” .

以下是附加到“ Canvas ”元素的事件处理程序。他们除了将数据转发给 webasm 应用程序外什么都不做。当然会在工作后清理干净。这是 Firefox 代码。 :)

canvas.addEventListener('pointerover', this.onPeOED, false);
canvas.addEventListener('pointerenter', this.onPeOED, false);
canvas.addEventListener('pointerdown', this.onPeOED, false);

canvas.addEventListener('pointermove', this.onPeMove, false);

canvas.addEventListener('pointerup', this.onPeUOCL, false);
canvas.addEventListener('pointerout', this.onPeUOCL, false);
canvas.addEventListener('pointercancel', this.onPeUOCL, false);
canvas.addEventListener('pointerleave', this.onPeUOCL, false);

canvas.addEventListener('mousedown', this.onMouseDown, false);
canvas.addEventListener('mouseup', this.onMouseUp, false);

 onPeOED: (function(e) {
    var handled = 0;
    var code = e.type.charCodeAt(7);
    var target = e.currentTarget;
    var posX = e.clientX;
    var posY = e.clientY;

    var flags = (1 << (9 + 16)); // fPointerInside
    if(posX < 0 || posY < 0 || posX >= target.width || posY >= target.height) {
        flags = 0;
    }
    // o == over, e == enter, d == down,

    if(code == 100) { // 100 == 'd'
        flags |= (1 << (10 + 16)); // in contact << 16
    }

    target._mousePointerId_ = -1;

    switch(e.pointerType) {
        case 'mouse':
            target._mousePointerId_ = e.pointerId;
            if(code == 100) { // 100 == 'd' handled by onMouseDown
                target.setPointerCapture(e.pointerId);
                return;
            }
            flags |= 0x0100;  // UID_MOUSE << 8
            break;
        case 'pen':
            flags |= 0x0200;  // UID_STYLUS << 8
            break;
        case 'touch':
            flags |= 0x0300;  // UID_FINGER << 8
            break;
        default:
            Module.print("unknown UID");
            break;
    }

    if(code == 100) { // 100 == 'd'
        target.setPointerCapture(e.pointerId);
    }

    // Module.print("ON uid " + e.pointerType + " " + e.type + " buttons " + e.which + " inside " + ((flags & (1 << (9 + 16))) != 0));

    if(e.buttons != 0) {
        flags |= (e.buttons << 16) | (1 << (10 + 16)); // fPointerInContact
    }

    handled = ccall('onPointerFlagsOn', 'number', EngineConnector.number_11_Sig,
        //  target  type|flags    time      id     screenX  screenY   pageX    pageY   targetX  targetY  pressure
        [   target._CPPHandle_,
            (code | flags),
            e.timeStamp,
            e.pointerId,
            e.screenX,
            e.screenY,
            e.pageX,
            e.pageY,
            posX,
            posY,
            e.pressure
        ]);
    e.preventDefault();
}),

onMouseDown: (function(e) {

    var target = e.currentTarget;
    var posX = e.clientX;
    var posY = e.clientY;

    // Module.print("mouse ME down " + target._mousePointerId_);

    if(target._mousePointerId_ < 0 || posX < 0 || posY < 0 || posX >= target.width || posY >= target.height) {
        Module.print("mouse ignored");
        e.preventDefault();
        return; // ignore downs while outide element
    }
    var wentDown = (e.buttons & (~(target._mouseButtons_)));
    target._mouseButtons_ = e.buttons;

    // Module.print("mouse down " + e.buttons + " went down " + wentDown);

    handled = ccall('onPointerFlagsOn', 'number', EngineConnector.number_11_Sig,
        //  target    type    time      id     screenX  screenY   pageX    pageY   targetX  targetY  pressure
        [   target._CPPHandle_,
          // 'd'  UID_MOUSE                       fPointerInside  fPointerInContact
            (100 | 0x0100 | (1 << (9 + 16)) | (1 << (10 + 16)) | (wentDown << 16)),
            e.timeStamp,
            e.pointerId,
            e.screenX,
            e.screenY,
            e.pageX,
            e.pageY,
            posX,
            posY,
            e.pressure
        ]);
    // e.preventDefault();

}),
fireButtonUp: (function(e, buttons) {

    // Module.print("ME fire up true " + buttons );

    var ptrId = e.currentTarget._mousePointerId_;
    if(ptrId < 0) {
        return;
    }

    ccall('onPointerFlagsOff', 'number', EngineConnector.number_11_Sig,
        //  target    type    time      id     screenX  screenY   pageX    pageY   targetX  targetY  pressure
        [e.currentTarget._CPPHandle_,
            (117 | 0x0100 | (buttons << 16)),  // 117 == 'u' == 'up', 1 == UID_MOUSE
            e.timeStamp,
            ptrId,
            e.screenX,
            e.screenY,
            e.pageX,
            e.pageY,
            e.clientX,
            e.clientY,
            0,
        ]);

}),
// up/leave
onMouseUp: (function(e) {

    var target = e.currentTarget;
    var posX = e.clientX;
    var posY = e.clientY;

    if(posX < 0 || posY < 0 || posX >= target.width || posY >= target.height) {
        // we have to kill ALL the buttons
        EngineConnector.fireButtonUp(e,target._mouseButtons_ | (1 << 10));
        target._mouseButtons_ = 0;
    } else {
        var released = (target._mouseButtons_ & (~e.buttons)); // will leave the one we released
        target._mouseButtons_ = e.buttons;
        if(e.buttons == 0) {
            released |= (1 << 10); // fPointerInContact
        }
        EngineConnector.fireButtonUp(e,(released | (1 << 9)));
    }
    e.preventDefault();

}),

onPeMove: (function(e) {

    var target = e.currentTarget;
    var posX = e.clientX;
    var posY = e.clientY;

    var flags = (1 << 9);
    if(posX < 0 || posY < 0 || posX >= target.width || posY >= target.height) {
        flags = 0;
    }

    var pType =  e.pointerType.charCodeAt(0);
    if(pType == 109) { // 'm' == 109
        // MOUSE
        if(target._mousePointerId_ < 0) {
            e.preventDefault();
            return;
        }
    } else {
        Module.print("move !!!");
    }

    flags |= pType; // 'm', 'p', 't'

    handled = ccall('onPointerMove', 'number', EngineConnector.number_11_Sig,
        //  target    flags time      id     screenX  screenY   pageX    pageY   targetX  targetY  pressure
        [ target._CPPHandle_,
            flags,
            e.timeStamp,
            e.pointerId,
            e.screenX,
            e.screenY,
            e.pageX,
            e.pageY,
            posX,
            posY,
            e.pressure
        ]);
}),
onPeUOCL: (function(e) {

    var code = e.type.charCodeAt(7);
    var target = e.currentTarget;
    target._poinsePointerId_ = -1;

    switch(e.pointerType) {
        case 'mouse':
            target._poinsePointerId_ = e.pointerId;
            if(code == 117) { // 117 == 'u' mouseup handled by mouse event handler
                return;
            }
            code |= 0x0100;  // UID_MOUSE << 8
            // let leave and cancel get reported
            break;
        case 'pen':
            code |= 0x0200;  // UID_STYLUS << 8
            if(code == 117) {
                code |= ((1 << 16) << e.which);
            }
            break;
        case 'touch':
            code |= 0x0300;  // UID_FINGER << 8
            if(code == 117) {
                code |= ((1 << 16) << e.which);
            }
            break;
        default:
            Module.print("unknown UID");
            break;
    }

    var posX = e.clientX;
    var posY = e.clientY;

    if(posX >= 0 && posY >= 0 || posX < target.width || posY < target.height) {
        code |= (1 << (9 + 16));
    }

    // Module.print("off uid " + e.pointerType + " " + e.type + " buttons " + e.which + " inside " + ((code & (1 << (9 + 16))) != 0));

    var handled = 0;

    handled = ccall('onPointerFlagsOff', 'number', EngineConnector.number_11_Sig,
        //  target    type    time      id     screenX  screenY   pageX    pageY   targetX  targetY  pressure
        [e.currentTarget._CPPHandle_,
            code,
            e.timeStamp,
            e.pointerId,
            e.screenX,
            e.screenY,
            e.pageX,
            e.pageY,
            posX,
            posY,
            e.pressure,
        ]);
    e.preventDefault();
}),

将近 30 年前,当我致力于让 Microsoft 创建 DirectX 时,我引用了一句 - 它又是 Deja Vous :)

“对于任何真正灵活的系统,必须能够使用它提供的设施从头开始实现该系统。”

最佳答案

我很惊讶没有人提到这个。 解决方案是 canvas.setAttribute('style','touch-action: none');

https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action

顺便说一句,在 Chrome 上可以正常工作 99%。

Firefox 仍会通过合成点击,我必须将其放入讨厌的事件计时代码中才能过滤掉。

两者都无法通过触摸模拟禁用 Windows 10 鼠标,因此无法同时使用鼠标和触摸。我还没有找到让用户在 Windows 10 上禁用它的方法。无论如何,我不想在整个系统范围内禁用它,而只是在点击和触摸/拖动 Canvas 元素时才禁用它。

关于javascript - 如何使用指针事件覆盖 firefox 和 chrome 中的触摸缩放和平移,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54621987/

相关文章:

javascript - 带有 js 扩展名的 React 文件的 GitHub 代码高亮显示

javascript - 输入 radio 选中/未选中在 Firefox 38.0.5 Windows 中不起作用-jquery

javascript - 更新 firefox 55 后,内联图像 (img) 未通过 SVG 在 HTML CANVAS 中呈现

javascript - chop 文本而不点击嵌套链接

java - 使用 BrowserMobProxy、Selenium、Firefox、marionette/gecko 获取请求和响应

CSS 未在 mozilla chrome 中显示

oracle - ssl_error_rx_record_too_long

javascript - 异步显示多个元素

javascript - 谷歌驱动器公共(public)

javascript - 谷歌分析 : Use multiple unknown trackers