如何设置mousemove更新速度?

Posted

技术标签:

【中文标题】如何设置mousemove更新速度?【英文标题】:How to set mousemove update speed? 【发布时间】:2011-07-12 14:52:50 【问题描述】:

我正在生成一个需要设置简单快速签名的函数。我正在画布字段中写签名。我用的是jQuery,但是mousemove坐标的刷新率不够快。发生的情况是,如果您将签名写入 fast,您会在写入的像素之间看到一些空白。

如何设置mousemove的刷新速度更快?

$("#xx").mousemove(function(e)

    ctx.fillRect(e.pageX - size, e.pageY - size, size, size);

    $("#pagex").html(e.pageX - size);
    $("#pagey").html(e.pageY - size);


【问题讨论】:

我认为,在鼠标移动时不需要清除画布来绘制签名。您只需要在画布上的最后一个鼠标位置和当前鼠标位置之间画一条线。 【参考方案1】:

你不能。 mousemove 事件由浏览器生成,因此您接收它们的速度与浏览器生成它们的速度一样快。

浏览器没有义务以任何给定的速率(移动的像素或经过的时间)生成事件:如果快速移动鼠标,您将看到报告坐标“跳跃”,因为浏览器报告“鼠标已移动,它现在在这里”,而不是“......并通过了这些像素”。事实上,速度较慢的计算机上的浏览器可能会生成较少的 mousemove 事件,以免页面变慢到爬行。

您可以做的是将 mousemove 事件中的连续位置与一条直线连接起来 - 这显然不会让您获得更高的精度,但它可以减轻影响。

【讨论】:

【参考方案2】:

你需要让你的处理程序更快。

如果事件的处理程序仍在运行,浏览器可以删除事件,因此您需要尽快退出 mousemove 处理程序。您可以尝试在那里优化代码或将工作推迟到鼠标移动完成之后。绘图可能是您正在做的最慢的事情,因此您可以将鼠标移动存储在内存中并稍后绘制。这在绘图完成之前不会更新显示,但它会更好地工作。

【讨论】:

【参考方案3】:

我建议(增强@river 答案):

    在 mousemove 事件处理程序中,只需将鼠标移动经过的那些点保存到某个缓冲区(数组)中,这样您的事件处理程序就会尽可能快 创建另一个函数,该函数将从缓冲区中读取这些点并将其绘制在画布上作为 lineTo() -> lineTo() -> lineTo() 以便连接所有点,它们之间没有空格。 将此绘图函数分配给 setInterval(),这样您的签名的绘制就不会等到用户完成“绘图”,但它会在用户手指移动后稍微延迟地绘制该签名

【讨论】:

我会修改它以在 requestAnimationFrame 循环中执行绘图功能,但尚未测试看看它是如何工作的【参考方案4】:

您可以根据计时器触发您自己的事件,这可能是个坏主意,但如果您真的需要它,那就更好了。

【讨论】:

【参考方案5】:

其他一些答案表明这是因为处理程序功能缓慢。在我的测试中,处理程序中是否只有count++ 或更昂贵的画布绘制调用没有任何区别——在这两种情况下,10 秒内生成的事件数量约为 500。但是,它可能会在较慢的计算机上有所作为。

Apparently 大多数鼠标/指针每秒向操作系统报告其位置的次数少于 100 次,因此这可能甚至不在浏览器的控制范围内。

您可能想研究新的PointerEvent.getCoalescedEvents() 方法。来自 MDN 文档:

PointerEvent 接口的getCoalescedEvents() 方法返回所有PointerEvent 实例的序列,这些实例合并到已调度的pointermove 事件中。

这是一个例子:

window.addEventListener("pointermove", function(event) 
  let events = event.getCoalescedEvents();
  for(let e of events) 
    draw(e.pageX, e.pageY);
  
);

但是,在对此进行测试之后,它似乎很少合并我计算机上的事件。不过,同样,它在速度较慢的计算机上可能更有用。所以目前最好的方法可能是使用ctx.lineTo,或者类似的方法(也许是arcTo)。这是一个简单的画布绘图演示,结合了getCoalescedEventslineTo

<canvas id="canvas" style="touch-action:none; width:100vw; height:100vh; position:fixed; top:0; left:0; right:0; bottom:0;"></canvas>

<script>
  let mouseIsDown = false;

  let ctx = canvas.getContext("2d");
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;


  window.addEventListener("pointerdown", function(e) 
    ctx.beginPath();
    ctx.moveTo(e.pageX, e.pageY);
    mouseIsDown = true;
  );
  window.addEventListener("pointerup", function(e) 
    mouseIsDown = false;
  );
  window.addEventListener("pointermove", function(event) 
   if(mouseIsDown) 
      let events = event.getCoalescedEvents();
      for(let e of events) 
        ctx.lineTo(e.pageX, e.pageY);
        ctx.stroke();
        ctx.beginPath();
        ctx.moveTo(e.pageX, e.pageY);
      
   
  );
</script>

【讨论】:

【参考方案6】:

您是否尝试过使用passive: truecapture: true 监听器?通常浏览器会为preventDefault() 调用等待 50-200 毫秒,但使用passive: true 选项会消除这种行为,代价是丢失preventDefault()。这种滞后就是为什么@vageko4924 在 10 秒内总共看到了大约 500 个事件,尽管处理程序的效率很高。 capture: true 选项只是确保您的回调在所有其他人之前触发 - 这可以保护您免受缓慢回调的间歇性延迟。

我不确定这在 jQuery 中会是什么样子,但它在 vanilla JS 中:

let x = document.querySelector('#xx'); // which would be faster if it were using getElementById()
x.addEventListener('mousemove', e => 
    // Your logic here
, passive: true, capture: true);

来源:https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener

【讨论】:

以上是关于如何设置mousemove更新速度?的主要内容,如果未能解决你的问题,请参考以下文章

Qt 中的 GLWidget 仅在 mousemove 上更新

Javascript和jquery事件--鼠标移动事件mousemove

WPF MouseMove InvalidateVisual OnRender 更新非常慢

js按下鼠标mousedown并mousemove的时候,如何保持鼠标样式全屏不变?

如何在不重绘整个视图的情况下更新 NSView 的一部分

VB中如何实现图片自动缩放