JavaScript 事件监听器 "pointerMove": points per second

标签 javascript addeventlistener dom-events event-listener

我有一个添加了“pointerMove”EventListener 的元素。现在,当四处移动鼠标时,我可以通过计算自“pointerDown”以来绘制的点总数并将其除以自“pointerDown”以来耗时来测量“pointerMove”每秒传送的数据点数 (pps)。 到目前为止,一切都很好。但奇怪的是,当开发人员控制台打开时,我获得了更高的 pps 率。

示例:按下我的鼠标按钮,然后乱七八糟地四处移动光标给我大约 60pps。但是当打开开发人员控制台然后执行完全相同的操作时,我的 pps 上升到大约 235 - 几乎增加了 400%!

此测试是在 Windows 10 上的 Chrome 76 中完成的。使用 Firefox 可能会获得类似的结果。此问题还涉及通过触摸或笔进行的输入,并且也存在于 Chrome 操作系统中(在其他操作系统上的行为尚未检查)。有趣的是,Microsoft Edge 似乎并未受到影响。

所以问题是:为什么会发生这种情况?以及如何在不打开开发者控制台的情况下获得更多的 pps?


此处的可执行示例: https://jsfiddle.net/Galveston01/unxmrchw/

var pointerid = undefined;
var start, count;
var canvas;

function startup() {
  canvas = document.getElementById("canvas");
  canvas.addEventListener("pointerdown", pointerdown, false);
  canvas.addEventListener("pointermove", pointermove, false);
  canvas.addEventListener("pointerup", pointerup, false);
  canvas.addEventListener("pointercancel", pointerup, false);
  canvas.addEventListener("touchstart", touch, false);
  canvas.addEventListener("touchmove", touch, false);
  canvas.addEventListener("touchend", touch, false);
  canvas.addEventListener("touchcancel", touch, false);
}

function pointerdown(event) {
  event.preventDefault();
  rect = canvas.getBoundingClientRect();
  if ((event.pointerType == "pen" || event.pointerType == "mouse") && pointerid == undefined) {
    pointerid = event.pointerId;
    var x = event.clientX - rect.left,
      y = event.clientY - rect.top;
    start = new Date().getTime();
    count = 1;
  }
}

function pointermove(event) {
  event.preventDefault();
  if (pointerid == event.pointerId) {
    var x = event.clientX - rect.left,
      y = event.clientY - rect.top;
    count++;
  }
}

function pointerup(event) {
  event.preventDefault();
  if (pointerid == event.pointerId) {
    var x = event.clientX - rect.left,
      y = event.clientY - rect.top;
    pointerid = undefined;
    count++;
    console.log((count / (new Date().getTime() - start) * 1000) + "pps");
  }
}

function touch(event) {
  if (pointerid != undefined) {
    event.preventDefault();
  }
}
startup();
<div id="canvas" style="width:2000px; height:2000px; ">
</div>

最佳答案

规范现在鼓励浏览器 vendor 对大多数 UI 事件设置阈值以提高性能。

例如,您可以在 mousemove UI-Event specs 中找到此类通知:

Implementations are encouraged to determine the optimal frequency rate to balance responsiveness with performance.

PointerEvents' pointermove specs drafts 中大致相同

These events may be coalesced or aligned to animation frame callbacks based on UA decision.

事实上,Firefox 和 Chrome 目前都在动画帧回调中合并这些事件,即它实际上与显示器的刷新率保持一致。

这也意味着 对于 pointermove 我们可以使用 PointerEvent.getCoalescedEvents 检索所有这些事件方法。

const canvas = document.getElementById("canvas");
let
  count = 0,
  start = 0;
const opts = {passive: true};
canvas.addEventListener("pointerdown", pointerdown, opts);
canvas.addEventListener("pointermove", pointermove, opts);
canvas.addEventListener("pointerup", pointerup, opts);
canvas.addEventListener("touchstart", prevent);
canvas.addEventListener("touchmove", prevent);


function pointermove(event) {
  if(canvas.hasPointerCapture(event.pointerId)) {
    const coalesced = event.getCoalescedEvents();
    count += coalesced.length;
    points.push(...coalesced);
    draw();
  }
}
function pointerdown(event) {
  canvas.setPointerCapture(event.pointerId);
  count = 1;
  start = new Date().getTime();
  points.length = 0;
}
function pointerup(event) {
  if(canvas.hasPointerCapture(event.pointerId)) {
    canvas.releasePointerCapture(event.pointerId);
    count++;
    const PPS = (count / (new Date().getTime() - start) * 1000);
    log.textContent = PPS + "pps";
  }
}

// just to show we have real Events
const ctx = canvas.getContext('2d');
const points = [];
function draw() {
  ctx.clearRect(0,0,canvas.width,canvas.height);
  ctx.beginPath();
  points.forEach(evt => {
    ctx.lineTo(evt.offsetX, evt.offsetY);
  });
  ctx.stroke();
}
// prevent default touch events or pointer ones are discarded
function prevent(evt) {
  evt.preventDefault();
}
#canvas {
  border: 1px solid;
}
<pre id="log"></pre>
<canvas id="canvas" width="2000" heigth="2000"></canvas>

请注意,在 Chrome 中,这些事件确实有自己的 时间戳,因此您可以知道它们应该在何时触发,但在 Firefox 中,此属性设置为 0.. .

但是,pointerrawupdate 事件 is coming in the drafts并且已经在 Chrome 中以Experimental Web Platform features 标记提供。
此事件不会与动画帧对齐,而是“尽快”触发。

if(!('onpointerrawupdate' in window)) {
  console.error("Your browser doesn't support 'pointerrawupdate' event. You may need to toggle some config flags");
}
else {
const canvas = document.getElementById("canvas");
let
  count = 0,
  start = 0;
const opts = {passive: true};
canvas.addEventListener("pointerdown", pointerdown, opts);
canvas.addEventListener("pointerrawupdate", pointermove, opts);
canvas.addEventListener("pointerup", pointerup, opts);


function pointermove(event) {
  if(canvas.hasPointerCapture(event.pointerId)) {
    const coalesced = event.getCoalescedEvents();
    count += coalesced.length;
  }
}
function pointerdown(event) {
  canvas.setPointerCapture(event.pointerId);
  count = 1;
  start = new Date().getTime();
}
function pointerup(event) {
  if(canvas.hasPointerCapture(event.pointerId)) {
    canvas.releasePointerCapture(event.pointerId);
    count++;
    const PPS = (count / (new Date().getTime() - start) * 1000);
    log.textContent = PPS + "pps";
  }
}

// Removed the drawing part because drawing should be made in animation frames
}
#canvas {
  border: 1px solid;
}
<pre id="log"></pre>
<canvas id="canvas" width="2000" heigth="2000"></canvas>

但在您的情况下(绘图应用程序),您最好坚持使用 getCoalescedEvents,因为无论如何您的绘图应该只发生在动画帧中。


Ps:关于为什么打开开发工具时阈值被停用,这可能是浏览器的错误。

关于JavaScript 事件监听器 "pointerMove": points per second,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57711515/

相关文章:

javascript - 下拉检查列表单选取消选择

javascript - 使用 addEventListener 加载 HTML 内容时出现问题

javascript - 在 div 滚动时动态检查 div 的 offsetTop

javascript - 调整到 0 高度时未触发调整大小事件?

javascript - 使用函数表达式的困难

javascript - 在ZK中处理MouseScroll

javascript - 旋转木马幻灯片(未滑动)事件的火灾事件,Bootstrap 3

javascript - 如何在 JavaScript 中清晰地呈现 JSON 中格式正确的名称键和值?

javascript - 我们如何实现 CssClass active on link Button Click

javascript - addEventListener 点击不起作用