Why does canvas2d context no longer fill ellipses?

I am working on a javascript game that simulates gravitational forces. It uses an HTML5 canvas element to draw 2D ellipses for planets. I am testing my game on Google Chrome. Here's a link to the game: http://gravitygame.hostingsiteforfree.com/index.php?page=playHTML

Until May 24th, everything went fine. However, after Chrome is updated from 26.0.1410.64 to 27.0.1453.94, filled ellipses are sometimes not drawn. This does not happen every time I download my game, and I never interrupted it while working on the local computer.

Here is a screenshot of the game: enter image description here

And here is a screenshot that shows that it does not fill in ellipses: enter image description here

I can’t say what is happening. I will include the part of the cycle that draws all the planets. I changed it for readability.

var i = bodies.length; while(i--){ var I = bodies[i]; var planetRad = (I.width/2)*_scale; if(_showTrails){ //draw the planet trail } if(//the planet is completely off the screen){ //draw a red planet on the edge of the screen ctx.beginPath(); ctx.arc(nX, nY, 2.5, 0, TWOPI); ctx.fillStyle = offScreenColor; ctx.fill(); ctx.strokeStyle = offScreenOutline; ctx.stroke(); } else{ //draw planet ctx.beginPath(); ctx.arc(nX, nY, (I.width/2)*_scale, 0, TWOPI); ctx.closePath(); ctx.fillStyle = I.bodyColor; ctx.fill(); } if(_showMotionVector){ //draw a line from the center of a planet showing the direction and speed it travelling ctx.strokeStyle = motionColor; ctx.beginPath(); ctx.moveTo(I.getScX(), I.getScY()); ctx.lineTo(I.motion.x * _scale * 12 + I.getScX(), I.motion.y * _scale * 12 + I.getScY()); ctx.stroke(); } } 

Why does it suddenly break?

+6
source share
1 answer

I looked at your online code and found that you are using setInterval for the animation loop.

Most likely, the reason is that the code cannot complete the calc call, etc., you risk the call stack - for context, this means that you can have a path that reset each other.

Try replacing setInterval with setTimeout . Of course, you need to restart it again from within the code - even better, put everything in a function using setTimeout at the end of this function, i.e.:

 function animate() { //... calcs and redraws which you have in setInterval setTimeout(animate, 0); } animate(); 

I use 0 for a timeout here for this test. setTimeout/setInterval will not sync with the refresh rate of the screen anyway.

If this works, then you know the reason. The next step would be to replace it with requestAnimationFrame , but let me know how this happens.

In an attempt to illustrate the problem, we can look at this illustration:

Working interval

Each block is a function within a cycle, and one cycle is one. Remember that setInterval calls at fixed intervals, while setTimeout calls a relative call when it calls. In this example, the functions are performed within the time budget, so that everything is going well.

In the following illustration:

Stacking interval

costs are out of budget, so setInterval is called again and the next call queues in the second loop until the first is completed. When a queue is processed between calls, you end up risking having two functions working in the context at the same time (or coming in a different order than you might expect).

Javascript, of course, is single-threaded, so they are not executed at the same time, but one of them is held on hold - if the first block for the next queue is called before the last block has time to call, then the first block will change the context and, possibly, even change the path to the last call of the previous call. Over time, the lag will increase and potentially (if only some additional available processing resources do not decide the queue in the queue, then in a more busy system this will be less likely) worsen and worsen as more stacking occurs.

Those. in this case, you could add lines to the context with beginPath() before the arc is full.

(hope this made some sense ...)

Using setTimeout prevent this, since it will not be executed before all calls in the animation loop return. The best option is to use requestAnimationFrame , as this will cause synchronization with the refresh rate of the screen, as well as whenever possible. It is lower level and therefore also more efficient.

Another way (no pun intended) is to use Web workers to do the calculations. This will be multi-threaded and can increase overall performance since the web worker does not affect the user interface flow.

+5
source

Source: https://habr.com/ru/post/946042/


All Articles