如何设置鼠标移动更新速度?

22

我正在生成一个函数,需要快速轻松地设置一个签名。我在画布字段中编写签名。我使用jQuery来实现,但是mousemove坐标的刷新率不够快。如果您写签名得太快,您会看到一些写入的像素之间的空白。

如何设置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);

}

我认为,在绘制签名时,没有必要在鼠标移动期间清除画布。您只需要在画布上在上一个鼠标位置和当前鼠标位置之间绘制一条线即可。 - Kira
你需要使用二分查找来加速它。 - chovy
6个回答

17

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

浏览器没有义务以任何给定的速率(无论是像素移动还是经过的时间)生成事件:如果你快速移动鼠标,你会发现报告了坐标的“跳跃”,因为浏览器报告的是“鼠标已移动,现在在这里”,而不是“…并通过这些像素”。实际上,在慢速计算机上运行的浏览器可能会生成更少的mousemove事件,以免页面变得缓慢。

你可以做的是将mousemove事件的连续位置连接成一条直线 - 这显然不会给你带来更多的精度,但它可能会减轻影响。


7

您需要加快处理程序的速度。

如果事件的处理程序仍在运行,浏览器可能会丢弃该事件,因此您需要尽快退出mousemove处理程序。您可以尝试优化那里的代码或将工作延迟到鼠标移动完成后再进行。绘图可能是您正在执行的最慢的操作,因此您可以将鼠标移动存储在内存中,并稍后进行绘制。这不会更新显示,直到绘图完成,但它在其他方面效果更好。


7
我建议(详细说明@river的答案):
  1. 在mousemove事件处理程序中,将鼠标移动通过的点保存到某个缓冲区(数组)中,使您的事件处理程序尽可能快
  2. 制作另一个函数,该函数将从缓冲区中读取这些点并将其绘制在画布上作为lineTo() -> lineTo() -> lineTo(),因此所有点都将连接,它们之间没有空白。每次调用此函数时,它将从缓冲区中读取几个点,并在它们之间绘制一条线。(绘制后不要忘记从缓冲区中删除它们)
  3. 将此绘图函数分配给setInterval(),以便签名的绘制不会等待用户完成“绘制”,而是在用户手指移动后稍有延迟地绘制该签名

我会修改这个程序,使其在requestAnimationFrame循环内执行绘图功能,但还没有测试过它的工作方式。 - EoghanM

4
一些其他答案建议这是由于处理程序函数速度较慢。在我的测试中,无论我在处理程序中只是使用count++,还是使用更昂贵的canvas绘图调用,10秒钟内生成的事件数量都约为500。但是,在速度较慢的计算机上可能会有所不同。 显然,大多数鼠标/指针每秒仅向操作系统报告其位置少于100次,因此这可能是甚至不在浏览器控制范围内的事情。
您可能需要研究新的PointerEvent.getCoalescedEvents()方法。来自MDN文档:

PointerEvent接口的getCoalescedEvents()方法返回所有合并到分派的pointermove事件中的PointerEvent实例序列。

以下是一个示例:
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>


2
你尝试过使用passive: truecapture: true监听器吗?通常浏览器会等待50-200毫秒来调用preventDefault(),但是使用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

addEventListener() 方法可以将指定的事件监听器注册到调用它的对象上,当该对象触发指定的事件时,指定的回调函数就会被执行。

该方法接受三个参数:

  • type: 字符串类型,表示要监听的事件类型。例如:"click" 或 "mousedown"。
  • listener: 要添加的事件处理函数。当事件被触发时,该函数会被调用。
  • options: 可选参数,一个包含有关监听器的配置属性的对象。可用的属性包括:captureoncepassive

在使用 addEventListener() 方法时,可以添加多个相同类型的事件监听器到同一元素上,这些监听器会按照添加顺序依次执行。也可以使用 removeEventListener() 方法来移除指定的事件监听器。


2

如果你真的需要,你可以根据计时器自己触发事件,虽然这可能不是一个好主意,但总比没有好。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接