为什么这个脚本在Javascript中滞后延迟

Why does this script lag delay in Javascript?

本文关键字:滞后 延迟 Javascript 脚本 为什么      更新时间:2023-09-26

我制作了一个脚本,其中应该有实时相互吸引的小球。问题是它非常慢。我使用了动画帧,所以我认为它应该更新每一帧,但事实并非如此。这是代码:

$(function() {
  var mouseDown
  var c = document.getElementById('myCanvas');
  var ctx = c.getContext("2d");
  var objects = []
  c.addEventListener("mousedown", onMouseDown);
  c.addEventListener("mouseup", onMouseUp);
  function createSquare(x, y, size, direction, xVel, yVel) {
    this.x = x;
    this.y = y;
    this.size = size;
    this.drawStylus = drawStylus;
  };
  function drawStylus() {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
    ctx.fill();
  };
  function getDistance(x1, y1, x2, y2) {
    return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
  }
  function draw() {
    ctx.clearRect(0, 0, 5000, 5000);
    for (i = 0; i < objects.length; i++) {
      var x = objects[i][0]
      var y = objects[i][1]
      var size = objects[i][2]
      var dir = Math.random() * Math.PI * 2
      var force = 0
      var xVel = 0
      var yVel = 0
      for (n = 0; n < objects.length; n++) {
        if (n != i) {
          force = 100 * objects[n][2] / getDistance(x, y, objects[n][0], objects[n][1])
          angle = Math.atan2(y - objects[n][1], x - objects[n][0])
          xVel += force * -Math.cos(angle)
          yVel += force * -Math.sin(angle)
          window.requestAnimationFrame(draw)
        };
      };
      ctx.beginPath();
      ctx.arc(x + xVel, y + yVel, size, 0, 2 * Math.PI);
      ctx.fill();
    };
  };
  function onMouseDown() {
    mouseDown = true
    x = event.clientX
    y = event.clientY
    size = 100
    animation = function() {
      size = size + 20
      var cursorSquare = new createSquare(x, y, size);
      cursorSquare.drawStylus();
      anim = window.requestAnimationFrame(animation)
    };
    window.requestAnimationFrame(animation)
  };
  function onMouseUp() {
    if (mouseDown) {
      window.cancelAnimationFrame(anim)
      var newSquare = new createSquare(x, y, size);
      objects.push([x, y, size])
      mouseDown = false
    };
  };
  function loop() {
    draw();
    window.requestAnimationFrame(loop);
  };
  function init() {
    loop();
  };
  init()
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<canvas id='myCanvas' width="5000" height="5000" style="border:1px solid #000000;"></canvas>

您正在为每个对象调用 requestAnimationFrame,这是使用 requestAnimationFrame (RAF) 的错误方式。

每帧只应调用一次,而不是每个对象调用一次。

function mainLoop(time){ // main loop RAF will add the time in milliseconds to the arguments.
    ctx.clearRect(0,0,canvas.width,canvas.height); // clear
    draw(); // call the draw loop
    requestAnimationFrame(loop); // request next frame
}
requestAnimationFrame(loop); // request next frame

使用像ctx.arc这样的绘制函数非常慢。如果您渲染图像而不是ctx.drawImage,您将获得更好的性能。您可以创建一个画布,在该画布上绘制弧线,然后使用ctx.drawImage(canvasImage,...绘制该画布以获得更快的更新。

另一个答案建议你使用forEach,不要使用forEach或任何涉及回调的数组函数,因为它们比使用标准循环慢得多(for,while,do)

更新

随着浏览器世界的变化迅速,我在这种情况下测试了forEach的使用,在这种情况下,这个消息并不好。 与forwhiledo while相比,forEach仍然在每次迭代上增加了大量的额外开销

需要注意的重要一点(以及为什么我删除了最后一段)是开销是每次迭代的,如果你的迭代次数很少,每次迭代有大量代码,那么开销是微不足道的,不值得打扰,个人编码风格应该选择在这些情况下使用什么样式。

另一方面,如果有大量迭代和每次迭代的少量处理,则使用 forEach 将显着影响循环的性能。

这适用于Chrome,Edge和Firefox,所有这些都显示具有内联代码(不调用函数)的标准迭代(for循环)是最快的,下一个,比标准迭代慢10%是带有函数调用的标准迭代(如forEach),然后forEach每次迭代的额外开销超过2倍。 (每个测试使用15-20比1的代码平衡, 也就是说,迭代中的代码比迭代所需的最小代码长 15-20 倍。所以一行用于forforEach循环和循环内的10-15行代码。

如果您正在处理几千到数万个数组,那么差异就不值得打扰,如果您要处理数千到数百万的数组,则应避免forEach

注意:我没有在类型化数组上测试forEach,因为这不适用于这种情况。

测试于

  • Chrome 版本 50.0.2661.37 beta-m
  • 火狐 46.0b2
  • 边缘 25.10586

有几件事可能会有所帮助。

在开始循环之前,将 objects.length 从 for 循环中取出并分配给 var。目前,您在循环的每次交互中计算对象的长度。

最好使用 objects.forEach 来迭代数组。

最后,为什么 draw() 在两个 for 循环的底部调用自己?这将很快填满事件循环,并怀疑减慢的主要原因。