【问题标题】:Why do multiple setTimeout() calls cause so much lag?为什么多次 setTimeout() 调用会导致如此多的延迟?
【发布时间】:2010-11-18 02:13:40
【问题描述】:

我有一个复杂的动画序列,涉及 JavaScript 中的淡入淡出和过渡。在这个由四个同时变化的元素组成的序列中,每个元素都使用setTimeout

在 Internet Explorer 9 中测试,动画以实时速度运行(它应该需要 1.6 秒,而它恰好需要 1.6 秒)。任何其他浏览器都会严重滞后,动画时间为 4 秒(Firefox 3 和 4、Chrome、Opera),而在 IE 8 及更低版本中则为 20 秒。

IE9 怎么能跑得这么快,而所有其他浏览器都陷入泥潭?

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

编辑:为了详细说明对 cme​​ts 的响应,这里是代码的大纲:

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 非常混乱,因为我没有真正组织得当,但有一些评论,希望你能看到大致的想法。

【问题讨论】:

  • 你能详细说明一下吗?是动画本身很慢,还是由于等待超时发生,动画不同部分之间存在等待时间?此外,一些代码会有所帮助。
  • 动画的四个部分是:(1)用opacity淡出当前页面,(2)用opacity淡入新页面,(3)用@987654326淡化页面颜色@, (4) 使用left 滚动导航栏上的指针(是的,这是非常过分的,但主要是为了炫耀)。动画应该以 40FPS(超时间隔为 25ms)运行,但在较慢的浏览器中,每帧至少需要两倍的时间,并且有可见的跳跃。
  • 发布代码可能会让您感到痛苦,因为它现在太乱了......
  • 你可能在某个地方错过了一个跳舞的婴儿
  • 我认为一次发生的事情太多了,这与setTimeout 关系不大。您可能希望使用此页面来宣传 IE9 中的高级 Javascript 引擎,但如果您对其他浏览器能够处理它感兴趣,您可能需要稍微降低效果。 (或者以更好的方式来做。不过,我真的不会查看你凌乱的脚本进行优化。:))

标签: javascript firefox google-chrome lag


【解决方案1】:

几件事:

  1. 您的超时时间为 25 毫秒。这转换为 40fps,这是一个非常高的帧速率,可以尝试通过 javascript 实现。特别是对于涉及可能触发重排的 DOM 操作的事情。将其增加到 50 或 60。对于您正在制作的动画类型,15fps 应该足够流畅。您不是要在此处显示视频,只是在页面上移动内容。

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

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

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

  3. 正如 Ben 所提到的,使用单个 setTimeout 来调度函数会更便宜。就此而言,改用 setInterval 可能会提高代码清晰度(也可能不会,这取决于您的编码风格)。


补充答案:

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

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

  1. 行走的士兵仅由两帧动画组成,我只是在两幅图像之间切换。但是效果还是非常有说服力的。您不需要完美的动画,只要看起来令人信服即可。

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

【讨论】:

  • 再次感谢您的回答。我不知道您可以将函数传递给 setTimeout - 这将使将来的事情变得更容易!我一定会让你知道它是否有效。我还将帧速率减半(我确保所有动画的帧数都是 2 的幂次方,因为这样可以轻松缩放并保持数字的完美精度,除非我弄错了)
  • 好的,一切正常。 Firefox 仍然较慢,但我认为这只是 IE9 中更好的引擎本身,因为速度远没有以前那么糟糕(现在只需要比它应该多半秒的时间)。非常感谢您的帮助 - 我不知道您可以将函数传递给 setTimeout。
  • 这是一个非常好的答案(和示例)
  • @slebetman 您的答案链接不再可用。如果你还有的话,你能重新发布你的来源吗?
  • @UpTheCreek:40fps 在 2010 年是非常高的帧率 - 它会使 IE6 慢下来。是的,在 2010 年我们仍然必须支持 IE6 和 IE7
【解决方案2】:

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

你触发了很多 setTimeout 序列,我不确定 JS 引擎对此进行了怎样的优化.. 尤其是 IE

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

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

然后有一个单独的 setTimeout 处理程序来检查该全局对象,并减少每次迭代的延迟并在延迟变为 时调用该函数

你的全局事件看起来像这样:

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 毫秒)循环遍历这些函数,并减少延迟,直到它们完成并使用 params 作为函数的参数触发函数(检查 Function.call )。

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

要取消事件,只需从事件列表中取消设置属性..

构建一些方法来添加/删除队列事件,更新参数等等..

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

(只是一个想法)

【讨论】:

  • 我明天试试(因为这里现在是凌晨 3 点),然后告诉你结果如何。
猜你喜欢
  • 1970-01-01
  • 2016-12-19
  • 2014-09-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-03
  • 1970-01-01
相关资源
最近更新 更多