JavaScript画布函数没有警示框时无法工作

3

作为展示给潜在雇主的组合部分,我正在制作一些演示网页。
其中一个页面模拟了一个“等离子球”中的等离子弧,并旋转360度以查看“弧”。
只有在每次旋转后重绘弧线时,如果我在重绘后加上“alert”,然后在脚本运行时取消警报,它才能正常工作。没有警报,什么也不会发生。

有人能解释或解决这个问题吗?以下是重绘函数:

function reDraw(yAngle) //work in progress - redraw the arc through 360 degrees to see the path
{
   var xCs;
   var yCs;

   for(degrees=10; degrees<360; degrees+=1)//spin sphere through 360 degrees
   {   

      context1 = document.getElementById("canvas1").getContext("2d");

      context1.clearRect(0,0,600,600);

      radialGrad(1,'canvas1',300,300,300,300,300,0,0,1,colour1,colour2,0,0,600,600);

      firstMove=true;

      for(moves=0; moves<axisMove; moves++)
      {
         if(firstMove)//start at centre of sphere for each re-draw
         {
            xCs=300;
            yCs=300;
            firstMove=false;         
         }

         context1.beginPath();

         context1.moveTo(xCs,yCs);         

         spinAngle=(degrees/180*Math.PI);

         //we are spinning in the X-Z plane so resolve X-Z moves only - retrieve each positive or negative move from the axis move arrays

         xCs=xCs+(zCStep[moves]*Math.sin(spinAngle))+(xCStep[moves]*Math.cos(spinAngle));

         yCs=yCs+yCStep[moves];

         context1.lineTo(xCs,yCs);

         context1.shadowBlur=2;
         context1.shadowColor="grey";

         context1.strokeStyle="white";
         context1.stroke();

      }
      alert();   
   }

}

3
帧速率太快了,尝试使用“settimeout”来给每帧显示一定的时间。 - Matthew Mcveigh
@MatthewMcveigh:差不多了,这与(非HTML5)进度条的“问题”相同:没有重绘(几乎等同于“帧速率过高”)。 - GitaarLAB
2个回答

1
由于历史原因,Web浏览器在(javascript)函数(处理程序)结束时重绘内容(进度条、ajax-loading gif等也有同样的问题)。所以即使你的循环一个迭代需要一秒钟(导致1FPS),画布仍然不会重新绘制(页面其余内容也是如此)直到你的javascript结束。
小的附注:HTML5 Web Workers最大的优势之一是它们在自己的线程中运行,因此它们不会“阻塞浏览器”。
上述问题的(历史上)常见解决方案是使用计时器(对于你的特定情况来说setInterval似乎是最好的选择,或者setTimeout)以便让浏览器重新绘制内容(在本例中为canvas元素)。

编辑:
Stefano Ortisi在下面的评论中提醒我关于requestAnimationFrame并提供了一个很好的链接:http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
基本上,它存在的主要原因是为了不需要继续使用相同的计时器,具有浏览器优化的实现(与计时器相比),并且理想情况下与webworkers一起使用(额外的线程)。

起初,这个API是一个相当“活跃”的规范(这就是为什么我忘记它的原因),但到现在为止,尘埃似乎已经安定下来,足以创建一个很好的polyfill(请参见上面的链接)。
我同意,在2013年12月,这是比定义自己的计时器更好的解决方案,因为“如果您在不可见的选项卡中运行动画循环,则浏览器不会保持其运行状态,这意味着更少的CPU,GPU和内存使用,从而导致更长的电池寿命。”


2
值得一提的是,也可以检查一下requestAnimationFrame,链接为http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/。 - Stefano Ortisi
警报是一种“主机”方法,在浏览器中,“主机”通常是“window”(因此为window.alert())。一些浏览器会给你取消多个警报的选项,所以很可能javascript仍然会“暂停”(我称警报为贫民断点有充分的理由...),并将控制权交给主机(在这种情况下是浏览器),请求弹出警报窗口。您应该尝试设置一个间隔,调用一个函数,每次函数调用绘制一个帧(您需要存储一些状态以知道您在动画中的位置)。 - GitaarLAB
@GitaarLAB - 你会如何在我的代码中编写 'setTimeout/setInterval' 的暂停?我尝试的一切似乎都不起作用! - user3123418
或者我该如何使用“requestAnimationFrame”?我不理解它。 - user3123418
嗯,你不应该知道(至少)定时器吗?因为这是“展示给潜在雇主的作品集”的一部分。正如我在上面的评论中写的:“尝试设置一个间隔,调用一个函数,每个函数调用绘制一帧(你需要存储一些状态来知道你在动画中的位置)”。此外,您可能想要在SO上搜索几分钟,以找到大量关于定时器的示例和解释。如果这没有帮助,我认为最好提出一个新问题(并将这个(在我看来)的问题保留原样)。Paul Irish的链接也有工作示例! - GitaarLAB
显示剩余9条评论

0

根据其定义,<canvas>元素使用位图缓冲区。您可以将<canvas>视为具有可修改位图的<img>

脚本在该位图中绘制(但不在屏幕上)。浏览器在有机会时呈现该位图。

为了让浏览器有机会在某个步骤绘制您的位图,您应该以某种方式将执行权从脚本转移到浏览器:调用alert()、setTimeout/Interval或在动画帧中进行绘制(requestAnimationFrame)。

requestAnimationFrame基本上是请求重新绘制浏览器视图,这将在接下来的16ms左右(浏览器中最大的60 fps)发生。


你能否分享一下那16毫秒的参考/来源链接?我知道通过个人测试,20毫秒通常是极限,而30毫秒则适用于大多数其他任务(如等待新的(i)frame),这是我理想的跨浏览器值。 - GitaarLAB
我尝试的第一件事是setTimeout和setInterval - 没有成功。我还不理解requestAnimationFrame代码,但我会尝试一下。 - user3123418
我找到了链接,它在同一篇 Paul Irish 的文章中,这是 Stefano 链接的(请看我的回答)。 (旁注:哈哈,我从不知道我发现的 30 毫秒 实际上是由于 Firefox 中的一个 bug,所以现在我仍然会使用它来实现跨浏览器兼容性) - GitaarLAB
@GitaarLAB,60 fps 更多是平台限制,而不是浏览器限制。例如在 Windows 中,WM_PAINT 通常每秒钟会出现 60 次,在 Mac 上也有类似的限制。没有必要比这还要快地重新绘制。人眼也无法捕捉更频繁的变化。 - c-smile
72到75赫兹(无闪烁),但在“慢速”平板电视上为60赫兹(因此,最终80FPS @ 80HZ对人眼来说是“完美的”)。但我明白你的意思:)谢谢! - GitaarLAB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接