javascript - 如何使用指针事件覆盖 Firefox 和 chrome 中的触摸缩放和平移
问题描述
这也适用于其他浏览器,但主要目标是 Firefox 和 Chrome。
我已经注册了所有指针事件侦听器,并且必须注册鼠标向下和鼠标向上侦听器以获取按钮状态更改(指针事件中的按钮状态不准确或“和弦”)
但即使我在所有指针上执行 event.preventDefault():over,enter,down,up,out,and,leave 事件(pointerType mouse for down 除外,因为它会禁用 mousedown 和 mouse up 监听器)我还剩下问题:
多点触控(多于一根手指)将被接管并执行页面缩放操作,使我的应用程序中的多点触控无效。
如果页面上有滚动条,则触摸平移/滚动操作将接管单次触摸。
preventDefault() 未在操作系统(即:Windows 10)中正确实现,并且在我的元素上向下的指针也会生成合成鼠标向下并将鼠标合成移动到触摸的位置。我可以用杂乱的代码忽略合成的向下和向上事件),但它会将鼠标放在意想不到的位置,并防止在需要时同时使用鼠标和触摸作为单独的输入。(解决这个问题可能需要在 DOM 元素本身上设置一个标志,而不是基于每个事件,因为内部事件队列会在操作系统和浏览器调度的事件之间放置延迟 - 我已经在我的本机应用程序中解决了这个问题)
我正在使用 Web 开发人员控制台进行测试,以便记录状态 - 我不知道这是否会影响事情。
我在 Chrome 上也有同样的触摸平移/缩放缺乏控制。Chrome 和 Firefox 上的鼠标上/下行为明显不同,因此需要单独的代码路径。PK
对于代码迷 - 请注意,这是“webasm”(游戏或生活游戏)引擎的接口设备“前端”:)
目标是为进入画布元素的任何指针(未被另一个元素的处理程序捕获)获取“干净的原始”事件流 使用捕获的指针镜像所有受支持设备上所有按钮的状态,直到捕获丢失。还能够将鼠标和触摸(以及任何其他支持的设备)用作单独的设备,忽略所有合成事件并希望关闭我正在收听的一个设备被我正在收听的另一台设备模拟的任何设备(所以他们不不要打架)。还可以完全控制事件的处理方式,直到丢失指针的事件流捕获。我想捕获所有在触摸设备(平板电脑、面板、屏幕等)的元素上“主要”触摸之后和同时下降的所有触摸,并可选择让它们“
下面是附加到“画布”元素的事件处理程序。他们除了将数据转发到 webasm 应用程序之外什么都不做。当然,它工作后会被清理干净。这是火狐代码。:)
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 年前,当我致力于让微软创建 DirectX 时,我的一句话——这又是似曾相识 :)
“要使任何系统真正灵活,就必须能够利用它提供的设施从头开始实施该系统。”
解决方案
我很惊讶没有人提到这一点。解决方案是 canvas.setAttribute('style','touch-action: none');
https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action
顺便说一句,在 Chrome 上工作 99% 正常。
Firefox 仍然会通过合成点击,我必须将这些点击放入讨厌的事件计时代码中才能过滤掉。
两者都无法从触摸仿真中禁用 Windows 10 鼠标,从而无法同时使用鼠标和触摸。我还没有找到让用户在 Windows 10 上禁用它的方法。无论如何,我不想在系统范围内禁用它,但只有在点击和触摸/拖动画布元素时才禁用它。
推荐阅读
- javascript - 加载函数的不同目录
- python - 在使用 str.findall 时获取 pandas 系列中的索引
- python-3.x - 有没有办法通过在其他列上找到最佳过滤器来保留最大不同值
- kframework - 什么时候对 K 个配置单元进行类型检查?
- javascript - 为什么会返回来自 javascript 选择器的值但使用 jquery 未定义
- html - HTML div 结束标记出现过早关闭错误
- git - 我收到一条错误消息 rm: cannot remove '.git/index.lock': No such file or directory
- laravel - Laravel .htaccess 在部署主机上不起作用
- sql - String_split 函数不会在 SQL Server 2019 中拆分分隔符
- android - Android 删除首选项