【问题标题】:What is the maximum rate at which events are fired in a web browser?在 Web 浏览器中触发事件的最大速率是多少?
【发布时间】:2018-06-03 00:51:11
【问题描述】:

我正在尝试解决我正在制作的一个简单的像素画家应用程序中遇到的一些性能问题。当鼠标被按住并且一个 div 被悬停在上面时,那个 div 应该改变它的背景颜色。它做了什么!问题是当快速移动鼠标时,某些 div 会沿着半规则间隔跳过。这向我暗示了某种抽样问题。

我想知道 Web 浏览器的最大事件触发频率是多少,以及这是否在某些标准中指定(例如,与 ES6 相关)。

感兴趣的人的情况:

【问题讨论】:

  • 问题在于您的代码而不是事件

标签: javascript html performance mouseevent dom-events


【解决方案1】:

更有可能的是,当下一个事件出现时,您的绘画代码仍在运行,这会拖累您的 CPU。您只需要在处理程序中缓存鼠标输入,然后与事件异步进行绘制工作。合作多任务的风格。

【讨论】:

    【解决方案2】:

    据我所知,事件触发的速率没有任何标准。正如我所经历的,这取决于很多因素,包括当前用户的机器功率。

    看,我在这个画布上绘制了两行点,分别位于固定的 Y 位置和当前 X 位置。上面的一个被脚本尽可能频繁地更新,另一个是用 mouseMoved 事件绘制的。如您所见,结果几乎相同(即使是 MouseMove 中的点有时也更频繁) dots frecuency comparison 事件处理程序花费的时间影响很大,实际上我让方法等待 1 秒然后绘制,结果是点间距为 5cm,以相同的速度移动鼠标。

    因此,当 CPU 忙于事件处理程序时,它更有可能不关注新的事件触发器。我唯一的建议是停止事件传播,这样它就不会消耗任何资源并返回 false,这样浏览器就不会执行任何默认行为。

    【讨论】:

      【解决方案3】:

      使用<canvas> 可以帮助您实现更流畅的行为:

      const scale = window.devicePixelRatio || 1;
      const unit = 8;
      const scaledUnit = unit * scale;
      const canvas = document.getElementById('canvas');
      const ctx = canvas.getContext('2d');
      const offsetLeft = canvas.offsetLeft;
      const offsetTop = canvas.offsetTop;
      
      let drawing = false;
      
      canvas.setAttribute('width', canvas.offsetWidth * scale);
      canvas.setAttribute('height', canvas.offsetHeight * scale);
      
      canvas.onmousedown = (e) => {
        drawing = true;
        
        paintPixel(Math.floor((e.pageX - offsetLeft) / unit), Math.floor((e.pageY - offsetTop) / unit));
      };
      
      canvas.onmouseup = (e) => {
        drawing = false;
      };
      
      canvas.onmousemove = (e) => {
        if (drawing) {
          paintPixel(Math.floor((e.pageX - offsetLeft) / unit), Math.floor((e.pageY - offsetTop) / unit));
        }
      };
      
      canvas.onmouseleave = (e) => {
        paint = false;
      };
      
      function paintPixel(x, y) {
        ctx.fillRect(x * scaledUnit, y * scaledUnit, scaledUnit, scaledUnit);
      }
      body {
        margin: 0;
        font-size: 0;
      }
      
      #canvas {
        width: 100%;
        height: 100vh;
      }
      <canvas id="canvas"></canvas>

      但是,要完全避免这些间隙,您必须从一个光标位置到下一个光标位置画一条线,而不是绘制单个“像素”。

      我会使用Bresenham's line algorithm 来计算连续事件之间的所有点。像这样的:

      const scale = window.devicePixelRatio || 1;
      const unit = 8;
      const scaledUnit = unit * scale;
      const canvas = document.getElementById('canvas');
      const ctx = canvas.getContext('2d');
      const offsetLeft = canvas.offsetLeft;
      const offsetTop = canvas.offsetTop;
      
      let drawing = false;
      let lastX = null;
      let lastY = null;
      
      canvas.setAttribute('width', canvas.offsetWidth * scale);
      canvas.setAttribute('height', canvas.offsetHeight * scale);
      
      canvas.onmousedown = (e) => {
        drawing = true;
        lastX = Math.floor((e.pageX - offsetLeft) / unit);
        lastY = Math.floor((e.pageY - offsetTop) / unit);
        
        paintPixel(lastX, lastY);
      };
      
      canvas.onmouseup = (e) => {
        drawing = false;
      };
      
      canvas.onmousemove = (e) => {
        if (drawing) {
          const x = Math.floor((e.pageX - offsetLeft) / unit);
          const y = Math.floor((e.pageY - offsetTop) / unit);
          const w = Math.abs(x - lastX);
          const h = Math.abs(y - lastY);
          
          if (w === 0 && h === 0) {
            paintPixel(x, y);
          } else if (w > h) {
            lineLandscape(lastX, lastY, x, y);
          } else {
            linePortrait(lastX, lastY, x, y);
          }
        
          lastX = x;
          lastY = y;
        }
      };
      
      canvas.onmouseleave = (e) => {
        paint = false;
      };
      
      function paintPixel(x, y) {
        ctx.fillRect(x * scaledUnit, y * scaledUnit, scaledUnit, scaledUnit);
      }
      
      function lineLandscape(x0, y0, x1, y1) {
        if (x0 > x1) {
          [x0, x1] = [x1, x0];
          [y0, y1] = [y1, y0];
        }
        
        const dx = x1 - x0;
        const dy = Math.abs(y1 - y0);
        const yi = y0 > y1 ? -1 : 1;
        
        let D = 2 * dy - dx;  
        let y = y0;
      
        for (let x = x0; x <= x1; ++x) {
          paintPixel(x, y);
          
          if (D > 0) {
            y += yi;
            D -= 2 * dx;
          }
          
          D += 2 * dy;
        }
      }
      
      function linePortrait(x0, y0, x1, y1) {
        if (y0 > y1) {
          [x0, x1] = [x1, x0];
          [y0, y1] = [y1, y0];
        }
        
        const dx = Math.abs(x1 - x0);
        const dy = y1 - y0;
        const xi = x0 > x1 ? -1 : 1;
        
        let D = 2 * dx - dy;  
        let x = x0;
      
        for (let y = y0; y <= y1; ++y) {
          paintPixel(x, y);
          
          if (D > 0) {
            x += xi;
            D -= 2 * dy;
          }
          
          D += 2 * dx;
        }
      }
      body {
        margin: 0;
        font-size: 0;
      }
      
      #canvas {
        width: 100%;
        height: 100vh;
      }
      &lt;canvas id="canvas"&gt;&lt;/canvas&gt;

      如果您确实需要使用&lt;div&gt;s,您也可以调整该算法以配合您的方法。

      【讨论】:

        猜你喜欢
        • 2018-08-08
        • 2014-06-13
        • 2013-10-06
        • 2021-10-25
        • 2015-09-07
        • 2010-10-13
        • 2015-02-12
        • 2011-04-22
        相关资源
        最近更新 更多