为什么canvas2d上下文不再填充省略号?

时间:2013-05-28 20:06:11

标签: javascript html5 html5-canvas

我正在开发一款模拟引力的javascript游戏。它使用HTML5 canvas元素为行星绘制2D椭圆。我在谷歌浏览器中测试我的游戏。这是游戏的链接:http://gravitygame.hostingsiteforfree.com/index.php?page=playHTML

直到5月24日,它运作得很好。但是,Chrome从26.0.1410.64升级到27.0.1453.94后,有时不会绘制填充的椭圆。每次我加载游戏时都不会发生这种情况,而且我在本地运行时从未让它破裂。

以下是游戏的截图:enter image description here

这是一个屏幕截图,显示它没有填充省略号:enter image description here

我不知道发生了什么。我将包括绘制所有行星的循环部分。为了便于阅读,我对其进行了修改。

    var i = bodies.length;
    while(i--){
    var I = bodies[i];
    var planetRad = (I.width/2)*_scale;
    if(_showTrails){
        //draw the planet's 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's 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();
    }
}

为什么它会偶尔突然中断?

1 个答案:

答案 0 :(得分:5)

我查看了您的在线代码,发现您正在使用setInterval进行动画循环。

这很可能是代码无法完成调用计算等的原因。您冒着堆叠调用的风险 - 因为上下文意味着您可以拥有相互重置的路径。

首先尝试将setInterval替换为setTimeout。你当然需要在代码中再次重新触发它 - 更好的是,在函数结尾处将所有内容放入一个带有setTimeout的函数中,即:

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

我在此测试使用0表示超时。在任何情况下,setTimeout/setInterval都不会同步到屏幕刷新率。

如果有效,那么你知道原因。下一步是用requestAnimationFrame替换它,但请告诉我它是怎么回事。

为了说明问题,我们可以看一下这个例子:

Working interval

每个块表示循环内的一个函数,一个循环是一种颜色。请注意,setInterval固定的间隔调用,而setTimeout调用 relative 时调用它。在这个例子中,函数在时间预算内执行,因此一切顺利。

在下一个插图中:

Stacking interval

支出超出预算,因此再次调用setInterval并在第一个循环结束之前排队第二个循环。当在调用之间处理队列时,你最终冒着在"同一时间"处理上下文的两个函数的风险。 (或者以不同于你预期的顺序排列。)

Javascript当然是单线程的,因此它们不会同时执行,但是一个等待 - 如果在最后一个块有时间被调用之前调用下一个队列的第一个块,那么第一个块将修改上下文,甚至可能在调用上一次调用的最后一次调用之前更改路径。随着时间的推移,滞后将会增加并且潜在地(除非一些额外的可用处理资源现在解决队列,然后 - 在繁忙的系统上这不太可能发生)随着更多堆叠的发生而变得越来越糟。

即,在这种情况下,您可以在beginPath()填充之前将行添加到arc的上下文中。

(希望有任何意义......)

使用setTimeout可以防止这种情况,因为它不会在动画循环中的所有调用都返回之前执行。更好的选择是使用requestAnimationFrame,因为这将尽可能与屏幕刷新率同步调用。它更低级,因此也更有效率。

另一条路径(没有双关语)是使用Web-workers进行计算。这将是多线程的,并且可以提高整体性能,因为Web工作者不会影响UI线程。