我有一个添加了“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/