【问题标题】:Poor JS/jQuery performance, particularly in IE9 and FirefoxJS/jQuery 性能不佳,尤其是在 IE9 和 Firefox 中
【发布时间】:2012-10-19 15:58:55
【问题描述】:

我在使用下面的脚本时遇到了严重的视觉和性能问题。最大的问题是对象的动画变得非常生涩,在 IE9 中几乎是严重的,但在 Firefox 中却越来越烦人。

直到最近,它的速度一直很快 - 但我担心复杂性会减慢速度。奇怪的是,Sunspider benchmark 在我的 IE9 实例中比在 Firefox 中运行得更快。

脚本(这是一个更大的集合 *** 的 sn-p):

  1. 检查用户进程的 HTML5 会话存储日志 游戏。
  2. 根据舞台,在两个之间为对象设置动画 点使用crSpline
  3. 确保浏览器窗口跟随对象 在大画布上,通过 scrollLeft 等。
  4. 最后,它通过colorbox 加载一个弹出窗口。
  5. 关闭此框后,用户进度日志会相应增加,并且对象会再次移动。

我可以对我的代码进行明显的速度改进吗?有相当多的重复,我怎样才能减少呢?是否有我遗漏的无限循环运行?有什么软件可以用来分析 JS 的慢点吗?

***(我无法提供其他 JS 文件或 HTML,但我已确定此脚本是问题所在)


更新: 经过更多测试后,似乎是 step animate 函数(通过 scrollLeft 跟随窗口中的对象)导致了生涩的动画。删除它会大大改善情况。

然而,这不是一个可行的长期解决方案。一个快速的解决方法是在完成时调用 follow 函数,但这对于最终用户来说是一个不太流畅的体验,尤其是当对象移动更长的距离时。

那么,我将如何修改 step 函数以“更慢”/更有效地运行? 我猜这是因为它使用所有可用资源来跟踪对象毫秒。

(function ($) {

  sessionStorage.gameMainStage = 0 

  moveShip =  function() {

    switch (sessionStorage.gameMainStage)

{
  case '1':
    $("#object").animate(
      { crSpline: $.crSpline.buildSequence([[715, 425], [582, 524], [556, 646], [722, 688], [963, 629], [1143, 467]]) },{
      duration: 10000,
          step: function() {
            var mover = $('#object'),               
            posX = mover.position().left;
            posY = mover.position().top;

            $(window)
            .scrollLeft(posX - $(window).width() / 2)
            .scrollTop(posY - $(window).height() / 2);
          },
          complete: function() {
            $.colorbox({href:"dialog-1.html", width:"737px", height:"474px", iframe: true, overlayClose: false, escKey: false, close: ""});
          }
      }
    );
    break;

  case '2':
    $("#object").animate(
      { crSpline: $.crSpline.buildSequence([[1143, 467], [1343, 667], [1443, 367],  [1243, 167], [1499, 285]]) },
        {
          duration: 5000,
          step: function() {
            var mover = $('#object'),               
            posX = mover.position().left;
            posY = mover.position().top;

            $(window)
            .scrollLeft(posX - $(window).width() / 2)
            .scrollTop(posY - $(window).height() / 2);
          },
          complete: function() {
            $.colorbox({href:"dialog-2", width:"737px", height:"547px", iframe: true, overlayClose: false, escKey: false, close: ""});
          }

        }
    );
    break;

  case '3':
    $("#object").animate(
      { crSpline: $.crSpline.buildSequence([[1499, 285], [1922, 423]]) },
        {
          duration: 5000,
          step: function() {
            var mover = $('#object'),               
            posX = mover.position().left;
            posY = mover.position().top;

            $(window)
            .scrollLeft(posX - $(window).width() / 2)
            .scrollTop(posY - $(window).height() / 2);
          },
          complete: function() {
            $.colorbox({href:"dialog-3.html", width:"737px", height:"547px", iframe: true, overlayClose: false, escKey: false, close: ""});
          }

        }
    );
    break;  

  case '4':
    $("#object").animate(
      { crSpline: $.crSpline.buildSequence([[1922, 423], [2216, 578]]) },{
        duration: 5000,
          step: function() {
            var mover = $('#object'),               
            posX = mover.position().left;
            posY = mover.position().top;

            $(window)
            .scrollLeft(posX - $(window).width() / 2)
            .scrollTop(posY - $(window).height() / 2);
            }, 

          complete: function() {
            $.colorbox({href:"game-1.html", width:"737px", height:"547px", iframe: true, overlayClose: false, escKey: false, close: ""});
          }
        }
    );
    break;

  case '5':
    $("#object").animate(
      { crSpline: $.crSpline.buildSequence([[2216, 578], [2769, 904]]) },{
      duration: 5000,
          step: function() {
            var mover = $('#object'),               
            posX = mover.position().left;
            posY = mover.position().top;

            $(window)
            .scrollLeft(posX - $(window).width() / 2)
            .scrollTop(posY - $(window).height() / 2);
          }, 

          complete: function() {
            $.colorbox({href:"dialog-4.html", width:"737px", height:"547px", iframe: true, overlayClose: false, escKey: false, close: ""});
          }
      }
    );
    break;

  case '6':
    $("#object").animate(
      { crSpline: $.crSpline.buildSequence([[2769, 904], [3263, 903]]) },{
      duration: 5000,
          step: function() {
            var mover = $('#object'),               
            posX = mover.position().left;
            posY = mover.position().top;

            $(window)
            .scrollLeft(posX - $(window).width() / 2)
            .scrollTop(posY - $(window).height() / 2);
          }, 

          complete: function() {
            $.colorbox({href:"dialog-5.html", width:"737px", height:"547px", iframe: true, overlayClose: false, escKey: false, close: ""});
          }
      }
    );
    break;

  case '7':
    $.colorbox({href:"game-2.html", width:"500px", height:"600px", iframe: true, overlayClose: false, escKey: false, close: ""});
  break;

  case '8':
    $.colorbox({href:"dialog-6.html", width:"737px", height:"567px", iframe: true, overlayClose: false, escKey: false, close: ""});
  break;

  case '9':
    $("#object").animate(
      { crSpline: $.crSpline.buildSequence([[3263, 903], [4141, 820]]) },{
      duration: 5000,
          step: function() {
            var mover = $('#object'),               
            posX = mover.position().left;
            posY = mover.position().top;

            $(window)
            .scrollLeft(posX - $(window).width() / 2)
            .scrollTop(posY - $(window).height() / 2);
          }, 

          complete: function() {
            $.colorbox({href:"dialog-7.html", width:"737px", height:"547px", iframe: true, overlayClose: false, escKey: false, close: ""});
          }
      }
    );
    break;

  case '10':
    $("#object").animate(
      { crSpline: $.crSpline.buildSequence([[4141, 820], [4568, 949], [4447, 1175]]) },{
      duration: 5000,
          step: function() {
            var mover = $('#object'),               
            posX = mover.position().left;
            posY = mover.position().top;

            $(window)
            .scrollLeft(posX - $(window).width() / 2)
            .scrollTop(posY - $(window).height() / 2);
          }, 

          complete: function() {
            $.colorbox({href:"dialog-8.html", width:"737px", height:"434px", iframe: true, overlayClose: false, escKey: false, close: ""});
          }
      }
    );
    break;

  case '11':
    $.colorbox({href:"dialog-9.html", width:"737px", height:"567px", iframe: true, overlayClose: false, escKey: false, close: ""});
  break;

  case '12':
    $("#object").animate(
      { crSpline: $.crSpline.buildSequence([[4447, 1175], [4701, 1124], [4816, 822]]) },{
      duration: 5000,
          step: function() {
            var mover = $('#object'),               
            posX = mover.position().left;
            posY = mover.position().top;

            $(window)
            .scrollLeft(posX - $(window).width() / 2)
            .scrollTop(posY - $(window).height() / 2);
          }, 

          complete: function() {
            $.colorbox({href:"dialog-10.html", width:"900px", height:"687px", iframe: true, overlayClose: false, escKey: false, close: ""});
          }
      }
    );
    break;
}

};

})(jQuery);

【问题讨论】:

  • 只是来自您的基准测试页面的附注:This benchmark tests the core JavaScript language only, not the DOM or other browser APIs - 您的颜色框和动画几乎是纯 DOM。

标签: javascript jquery performance


【解决方案1】:

恐怕您使用的库太旧,无法期待出色的性能。

我在您的代码中没有发现任何问题(除了您可以为每个 switch 案例使用带有配置参数的函数,但这只是重构问题,不会显着影响性能)

CrSpline 使用左侧和顶部 CSS 属性。

您可能想研究利用硬件加速的 CSS 2d 转换:

使用-webkit/moz/ms-transform: translateX(-1000px) translateY(200px) 而不是left: -1000px; top: 200px;

我认为你可以很容易地朝着这个方向重写 crspline 库中的一些代码。

您也可以尝试寻找更新的“样条”库。

另外一点:crSpline 似乎没有使用 requestAnimationFrame 功能。 JQuery 的animate 方法也没有。我建议你看看 TweenLite/TweenMax 库:http://www.greensock.com/v12/

为你的工作干杯!

【讨论】:

  • CSS 2D 转换不保证是硬件加速的。为确保您获得硬件加速,您应该使用 translate3d(0, 0, 0),它告诉浏览器您将使用 Z 轴(并将启动 GPU)。您仍然可以使用 top 和 left 并获得硬件加速。 html5rocks.com/en/tutorials/speed/html5
  • True - translate3d 是硬件加速的,可以带来巨大的性能提升,但过于频繁地在太多元素上调用它们只会消耗加速资源。 2d 变换通常不会加速,但它们仍然可以大大提高性能,因为更改它们的值不会导致 dom 重新计算和重新渲染 - 这是动画最大的性能消耗。
【解决方案2】:

我在与 window.resize 和 window.scroll 挂钩的单页应用程序中遇到了这个问题。它在 IE 上似乎比其他浏览器慢得多。

我注意到的第一件事是,在 IE(具体是版本 8)中,附加到 window.scroll 或 .resize 的回调在调整窗口大小(或滚动)。因此,任何附加的回调都被调用多次,比 Chrome 增加了它的相对成本。

我们设法通过将这些回调中所做的事情最小化来解决我们的问题,而我们的主要收获是摆脱了 jQuery 选择器。因此,在您的情况下,例如 var mover = $('#object') 在回调函数中,每次事件触发时,IE 都会尝试查找并获取对象并将其包装在 jQuery 中,只需在外部执行一次您的回调,然后使用缓存的对象。在我们的案例中,这提高了数量级的性能,即使没有遇到性能问题(这是一个不必要地重复的操作),这似乎也是一件好事。

因此,以案例 8 为例,有如下内容:

 case '10':{
    //caching myObject once and then use it afterwards
    var myObject = $("#object"),
        $window = $(window);

    myObject.animate(
      {
      crSpline: $.crSpline.buildSequence([[4141, 820], [4568, 949], [4447, 1175]]) },
      {
      duration: 5000,
          step: function() {
            var mover = myObject, //no need to refetch the object               
            posX = mover.position().left;
            posY = mover.position().top;

            $window.scrollLeft(posX - $window.width() / 2)
            .scrollTop(posY - $window.height() / 2);
          }, 

          complete: function() {
            $.colorbox({href:"dialog-8.html", width:"737px", height:"434px", iframe: true, overlayClose: false, escKey: false, close: ""});
          }
      }
    );
}

ps:此外,我不确定您的应用程序的语义,但您可能需要添加自己的逻辑来跟踪 posX 和 posY,因为它们可能总是引用原始缓存对象,但在所有情况下,请执行我提到的步骤以确保选择器的成本是导致问题的原因(就像我的情况一样)。

【讨论】:

    【解决方案3】:

    首先,你应该优化你的代码,为你的动画创建一个函数(未经测试,但应该可以工作):

    function animateMyObjet(duration,sequence,callback)
    {
        $("#object").animate(
        {   crSpline: $.crSpline.buildSequence(sequence),
            {
              duration: duration,
              step: function() {
                var mover = $('#object'),               
                posX = mover.position().left;
                posY = mover.position().top;
    
                $(window)
                .scrollLeft(posX - $(window).width() / 2)
                .scrollTop(posY - $(window).height() / 2);
              },
              complete: function() {
                callback();
            }
        }
    }
    

    在你的代码中这样调用:

    switch (sessionStorage.gameMainStage)
    {
      case '1': animateMyObjet(10000,[[715, 425], [582, 524], [556, 646], [722, 688], [963, 629], [1143, 467]],                                                                                  
    function() {
    $.colorbox({href:"dialog-1.html", width:"737px", height:"474px", iframe: true, overlayClose: false, escKey: false, close: ""});
            break;
    
    case '2' : ...
    
    }
    

    其次,几周前我发现网页元素的数量正在影响 IE9 的性能。尝试隐藏所有不需要的元素,例如由于可滚动容器而隐藏的元素。

    有时,屏幕上可见元素的数量也会影响动画性能。对于测试,尝试减小浏览器窗口大小并播放动画。我很确定动画会更流畅。

    希望这些建议对您有所帮助。如果您解决了问题,请不要忘记发布您的最终解决方案!

    最后,查看一个关于 IE9 性能问题的潜在相关主题:IE9 : Always small CPU utilization on my web site

    【讨论】:

    • Web Developer 工具栏中的 Javascript 分析相关主题的链接非常有用,谢谢。
    【解决方案4】:

    您可以尝试检查 IE 是否不会尝试在与 IE9 浏览器模式和 IE9 文档模式不同的模式下工作,即怪癖或兼容模式。如果它确实试图强制它使用默认值。 Force IE compatibility mode off using tags

    【讨论】:

      【解决方案5】:

      我的想法是,如果您经常看到事件处理程序被调用,那么如果当前没有设置超时,那么事件处理程序只需设置一个超时(比如 10 毫秒),并在超时函数中包含移动的内容。

      【讨论】:

        猜你喜欢
        • 2011-11-24
        • 1970-01-01
        • 2011-06-18
        • 1970-01-01
        • 2013-06-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-02
        • 1970-01-01
        相关资源
        最近更新 更多