为什么多个setTimeout()调用导致如此多的延迟?

时间:2010-11-18 02:13:40

标签: javascript firefox google-chrome lag

我有一个复杂的动画序列,涉及JavaScript中的淡入淡出和过渡。在此序列中,由四个元素一次更改组成,每个元素都使用setTimeout

在Internet Explorer 9中测试,动画以实时速度运行(它需要1.6秒,而且需要1.6秒)。任何其他浏览器都会出现可怕的延迟,动画时间为4秒(Firefox 3和4,Chrome,Opera),IE 8及以下版本的浏览时间为20秒。

当所有其他浏览器陷入困境时,IE9怎么会这么快?

我试图找到将元素合并为一个的方法,以便在任何给定时间都有一个setTimeout,但不幸的是它不会受到任何干扰(例如点击不同的链接以启动新的当前动画完成之前的动画。)

编辑:为了回应评论,这里是代码的大纲:

link.onclick = function() {
    clearTimeout(colourFadeTimeout);
    colourFadeTimeout = setTimeout("colourFade(0);",25);

    clearTimeout(arrowScrollTimeout);
    arrowScrollTimeout = setTimeout("arrowScroll(0);",25);

    clearTimeout(pageFadeOutTimeout);
    pageFadeOutTimeout = setTimeout("pageFadeOut(0);",25);

    clearTimeout(pageFadeInTimeout);
    pageFadeInTimeout = setTimeout("pageFadeIn(0);",25);
}

四个函数中的每一个都将淡入淡出一帧,然后在参数递增的情况下设置另一个超时,直到动画结束。

你可以在http://adamhaskell.net/cw/index.html看到页面(用户名:knockknock;密码:goaway)(它有声音和音乐,可以禁用,但要注意!) - 我的JavaScript非常凌乱,因为我没有'确实正确地组织了它,但它有点评论,所以希望你能看到一般的想法是什么。

2 个答案:

答案 0 :(得分:12)

有几件事:

  1. 你的超时是25毫秒。这转换为40fps,这是一个非常高的帧速率,试图通过JavaScript实现。特别是对于涉及DOM操作的事物,可能会引发回流。将它增加到50或60.对于你正在做的各种动画,15fps应该足够流畅。你不是想在这里展示视频,只是在页面上移动。

  2. 不要将字符串用作setTimeout()的第一个参数。特别是如果你关心表现。这将强制javascript重新编译每帧动画的字符串。请改用功能。如果需要传递参数,请使用匿名函数来包装要执行的函数:

    setTimeout(function(){
        pageFadeIn(0)
    },50);
    

    这只会在加载脚本时编译一次。

  3. 如Ben所述,使用单个setTimeout来安排功能会更便宜。就此而言,代码清晰度可能会因使用setInterval而改善(或者可能不会,取决于您的编码风格)。


  4. 补充答案:

    编程javascript动画是关于优化和妥协的。可以在页面上制作很多东西,但速度很慢,但你需要知道如何正确地做,并决定要牺牲什么。作为一个可以动画多少动画的例子,我几年前就写过一个实时战略游戏。

    我为优化游戏所做的事情包括:

    1. 步行士兵只有两帧动画,我只是在两幅图像之间切换。但是效果非常令人信服。你不需要完美的动画,只需要一个看起来令人信服的动画。

    2. 我为所有内容使用了一个setInterval。它的CPU更便宜,更易于管理。只需决定一个基本帧速率,然后安排不同的动画在不同的时间开始。

答案 1 :(得分:3)

嗯,这是很多javascript(尽管“四倍剂量的令人敬畏”:):

你正在解决很多setTimeout序列,我不确定JS引擎是如何优化的...特别是IE< = 8

好吧,也许只是一个粗略的建议......你可以写一个小型计时引擎。

维护一个全局对象,该对象存储当前正在运行的定时事件以及要运行的函数和延迟......

然后有一个setTimeout处理程序检查该全局对象,并减少每次迭代的延迟,并在延迟变为< 0

你的全球事件会是这样的:

var events = {

        fade1 : {
            fn : func_name,
            delay : 25,
            params : {}
        }

        fadeArrow : {
            fn : func_name,
            delay : 500,
            params : {}
        }

        slideArrow : {
            fn : func_name,
            delay : 500,
            params : {
                arrow:some_value
            }
        }

    }

然后创建一个函数以一定间隔(可能是10或20 ms)循环遍历这些函数并减少延迟,直到它们完成并使用params作为函数的paramer来触发函数(检查Function.call)。 / p>

一旦关闭,再次以相同的延迟触发setTimeout ..

取消事件只是从事件列表中取消设置属性..

构建一些方法来添加/删除排队事件,更新params等等。

这会将所有内容减少到只有一个超时处理程序..

(只是一个想法)