【问题标题】:Javascript events responsiveness over a <canvas> element<canvas> 元素上的 Javascript 事件响应性
【发布时间】:2011-03-02 16:32:53
【问题描述】:

我正在编写一个在画布上操作视频的 html5 应用程序。 我还在它上面显示了一个自定义(自绘)鼠标光标。 为了更新我的应用程序,我调用 setInterval 函数将内容绘制到画布上。

在处理视频时,我需要:非常高的刷新率 + 大量的处理器时间来绘制。 (每帧约 50 毫秒)。

我的绘图功能导致我的鼠标功能饿死。 (这是我可以接受的)。

但是...在饥饿完成后,它会响应旧事件。鼠标最多可能需要 3 帧才能捕捉到,以便我可以在正确的位置渲染它。这意味着您可以在停止移动鼠标后看到光标“爬行”。

  1. 有没有办法让 onmousemove 事件比我的 setInterval(drawFunction) 具有更高的优先级?

  2. 在绘图功能中,我可以“询问”是否有待处理的鼠标事件,并撤销我当前的绘图调用吗?

我可以使用其他一些技巧吗? (我可以在 webWorker 中绘制后台缓冲区,但据我了解,这只是线程抽象 [线程不是并发的])

【问题讨论】:

  • 标题错误。这与 HTML5 无关,与 javascript 以及可能仅与画布有关。
  • @Rob: Canvas 是一个 HTML5 功能...
  • @the_drow - 是的,但问题很可能是关于 js,也许是 html5 的一个元素,而不是全部。
  • @Rob:更好?或者你想推荐另一个标题?
  • canvas 不是 html5 功能,它有自己的规范 ;)

标签: javascript html canvas


【解决方案1】:

你不能优先考虑事件处理,至少不能直接。

您可能会考虑让自己的计时器驱动代码检查挂起的鼠标事件。鼠标事件处理程序只会将工作请求放入队列中。您的视频操作可以只检查该队列并根据需要处理操作。因此,真正的鼠标工作也将在计时器代码中完成。

edit 这就是我的想法。您仍然有鼠标事件的处理程序:

var eventQueue = [];

canvasElement.onmousemove = function(evt) {
  evt = evt || event;
  eventQueue.push({ type: evt.type, x: evt.clientX, y: evt.clientY, when: new Date() });
};

因此鼠标处理程序不会做任何实际工作。他们只是将详细信息记录在事件列表中,然后返回。

然后,计时器驱动的主循环可以检查事件队列并决定要做什么。例如,如果它看到一整串“mousemove”事件,它可以计算所有这些事件的净位置变化并只更新一次光标位置。

【讨论】:

  • 这可以解决我的问题。但是我将如何检查未决的鼠标事件?我没能找到这个 API。我只能通过事件获取鼠标坐标。
  • 你的鼠标事件仍然有处理程序 - 我会更新我的答案。
  • 此解决方案无济于事。问题是直到我的 setTimer 函数结束时才调用我的 onmousemove 函数。此外,据我所知,下一个 setTimer 函数将在 onMouse 事件之前调用。现在,如果我知道有未处理的鼠标事件,那么我可以跳过我的 setTimer 函数。
  • 那么您可能想使用setTimeout() 而不是setInterval()。当您使用setTimeout() 时,您可以保证您将在定时循环的一次迭代和下一次迭代之间留下一个间隙。在此期间,鼠标事件可以通过并由我建议的小而快的处理程序处理。
  • 对可能需要很长时间的事情使用setInterval 是一个坏主意,如果在达到下一个间隔时您的处理还没有完成,您的呼叫将得到备份,并且系统将变得高度无响应。对于这种工作,您应该始终使用setTimeout,这对于何时再次运行很明智。如果可以的话,你应该使用developer.mozilla.org/en/DOM/window.requestAnimationFrame,这正是这种事情的用意
【解决方案2】:

您当然可以使用 Web Worker 在后台操作像素。 Web Workers并发运行。它似乎不是规范的一部分,但每个实现都在单独的进程中运行工作人员。因此,您的主脚本将负责更新您的自定义光标,但您会将画布 ImageData 传递给后台工作人员进行视频处理。请注意,您不能将实际的 Canvas 或 Context 发送给工作人员,因为工作人员没有 DOM 访问权限。

【讨论】:

    【解决方案3】:

    没有办法给事件更高的优先级,它们似乎是先到先得的基本服务。假设您正在使用setTimeout,可能有帮助的一件事是将您的任务分解成更小的可重新启动的块,例如如果您正在处理这样的图像:

    for(y=0; y<height; y++) {
       // Deal with rows of pixels
    }
    // Show image
    

    你可以这样做:

    var INTERVAL = height/4;
    
    for(y = old_y; y<height && y<old_y+INTERVAL ; y++) {
      // Deal with row of pixels
    }
    if (y == height) {
      // show image
    }
    old_y = (y == height) ? 0 : y;
    

    然后其他事件将有 4 倍(或其他,取决于 INTERVAL)更大的机会被处理。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-08-16
      • 1970-01-01
      • 2012-03-04
      • 2018-06-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多