我制作了一个脚本,其中应该有实时相互吸引的小球。问题是它非常慢。我使用了动画帧,所以我认为它应该更新每一帧,但事实并非如此。这是代码:
$(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的使用,在这种情况下,这个消息并不好。 与for
、while
和do while
相比,forEach
仍然在每次迭代上增加了大量的额外开销
需要注意的重要一点(以及为什么我删除了最后一段)是开销是每次迭代的,如果你的迭代次数很少,每次迭代有大量代码,那么开销是微不足道的,不值得打扰,个人编码风格应该选择在这些情况下使用什么样式。
另一方面,如果有大量迭代和每次迭代的少量处理,则使用 forEach 将显着影响循环的性能。
这适用于Chrome,Edge和Firefox,所有这些都显示具有内联代码(不调用函数)的标准迭代(for循环)是最快的,下一个,比标准迭代慢10%是带有函数调用的标准迭代(如forEach),然后forEach每次迭代的额外开销超过2倍。 (每个测试使用15-20比1的代码平衡, 也就是说,迭代中的代码比迭代所需的最小代码长 15-20 倍。所以一行用于for
,forEach
循环和循环内的10-15行代码。
如果您正在处理几千到数万个数组,那么差异就不值得打扰,如果您要处理数千到数百万的数组,则应避免forEach
。
注意:我没有在类型化数组上测试forEach
,因为这不适用于这种情况。
测试于
- Chrome 版本 50.0.2661.37 beta-m
- 火狐 46.0b2
- 边缘 25.10586
有几件事可能会有所帮助。
在开始循环之前,将 objects.length 从 for 循环中取出并分配给 var。目前,您在循环的每次交互中计算对象的长度。
最好使用 objects.forEach 来迭代数组。
最后,为什么 draw() 在两个 for 循环的底部调用自己?这将很快填满事件循环,并怀疑减慢的主要原因。