【问题标题】:Why is my CSS3/jQuery animation's performance so slow?为什么我的 CSS3/jQuery 动画的性能这么慢?
【发布时间】:2011-12-22 11:48:35
【问题描述】:

为什么这么慢?我用 css3 制作了一个等距立方体网格,鼠标悬停时上下摆动。它在网格大小小于 12 左右的 Firefox 上运行良好,而在大约 18 左右的 Chrome 上运行良好。我有一个不错的视频卡和 CPU,让我烦恼的是为什么只为一个立方体的动画制作动画是如此缓慢如果我确保我只将鼠标悬停在一个立方体上。我的 JavaScript 是否需要优化,或者这只是浏览器 CSS3 和 JavaScript 引擎的当前实现所期望的?下面的完整测试用例包括动态更改网格大小的滑块,可以自己下载或访问由 Doug 慷慨提供的this jsfiddle version

<html>
    <head>
        <style type="text/css">
            body
            {
                background: black;
            }

            .cube
            {
                position: relative;
            }

            .cube .rightFace, .cube .leftFace
            {
                height: 25px; width: 10px; padding: 5px;
            }

            .leftFace
            {
                position: absolute;

                -webkit-transform: skew(0deg, 30deg);
                -moz-transform: skew(0deg, 30deg);
                -o-transform: skew(0deg, 30deg);

                -moz-box-shadow: rgba(0, 0, 0, 0.4) 1px 2px 10px;
                -webkit-box-shadow: rgba(0, 0, 0, 0.4) 1px 2px 10px;
                -o-box-shadow: rgba(0, 0, 0, 0.4) 1px 2px 10px;
                box-shadow: rgba(0, 0, 0, 0.4) 1px 2px 10px;

                border: 1px solid black;
            }

            .rightFace
            {
                -webkit-transform: skew(0deg, -30deg);
                -moz-transform: skew(0deg, -30deg);
                -o-transform: skew(0deg, -30deg);

                position: absolute;
                left: 19.5px;

                border: 1px solid black;
            }

            .topFace div
            {    
                width: 19px;
                height: 19px;


                border: 1px solid black;

                -webkit-transform: skew(0deg, -30deg) scale(1, 1.16);
                -moz-transform: skew(0deg, -30deg) scale(1, 1.16);
                -o-transform: skew(0deg, -30deg) scale(1, 1.16);
            }

            .topFace
            {
                position: absolute;

                left: 10.25px;
                top: -16.5px;

                -webkit-transform: rotate(60deg);
                -moz-transform: rotate(60deg);
                -o-transform: rotate(60deg);
            }

            #slider
            {
                width: 200px;
                margin: 0 auto;
            }
        </style>
        <link type="text/css" rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css" />
        <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js"></script>
        <script type="text/javascript">
            function refreshCubes()
            {
                $('.box').empty();
                var x = $("#slider").slider("value");
                var initialTop = 50;
                var initialLeft = 450;

                for(var i = 1; i < x; i++)
                {
                    for(var j = 1; j < x; j++)
                    {
                        var cube = $('<div class="cube"><div class="topFace"><div></div></div><div class="leftFace"></div><div class="rightFace"></div></div>');

                        cube.css(
                        {
                            left : initialLeft + (20 * i) + (-19 * j) + 'px',
                            top : initialTop + (11 * i) + (11 * j) + 'px'
                        });

                        cube.find('.topFace div').css('background', 'rgb(100,' + Math.ceil(255 - 16 * i) + ',' + Math.ceil(255 - 16 * j) + ')');
                        cube.find('.rightFace').css('background', 'rgb(35,' + Math.ceil(190 - 16 * i) + ',' + Math.ceil(190 - 16 * j) + ')');
                        cube.find('.leftFace').css('background', 'rgb(35,' + Math.ceil(190 - 16 * i) + ',' + Math.ceil(190 - 16 * j) + ')');
                        cube.children('div').css('opacity', '.9');

                        cube.hover(function()
                        {
                            $(this).animate({top: '-=25px'}, 400, 'easeInCubic');

                        }, function()
                        {
                            $(this).animate({top: '+=25px'}, 400, 'easeOutBounce');
                        });

                        $('.box').append(cube);
                    }
                }
            }

            $(document).ready(function()
            {    
                $('#slider').slider(
                {
                    value: 9,
                    max: 30,
                    min: 2,
                    slide: refreshCubes,
                    change: refreshCubes
                });

                refreshCubes();
            });
        </script>
    </head>
    <body>
        <div id="slider"></div>
        <div class="box"></div>
    </body>
</html>

【问题讨论】:

  • 下载?真的吗?请提供一个工作示例的链接或创建一个 jsfiddle 等。
  • 像这样:jsfiddle.net/TLZAE
  • 其实我是想说但最后忘了。谢谢你这样做:)
  • @Ambrosia,np。您可以将链接添加到您的问题中,以便人们可以轻松找到它:)
  • 这很有趣。它的速度很慢,网格尺寸更大。一定是因为是软件渲染???你在IE9+中试过吗? IE9 似乎具有用于 DOM 操作的硬件加速。我现在正在重新安装 IE9...

标签: jquery animation css


【解决方案1】:

这个更快:http://jsfiddle.net/JycdN/1/

我优化了 jQuery 代码本身。此外,为“+=25px”样式的 CSS 值设置动画是个坏主意,因为每次这样做都会迫使浏览器在动画计算之上进行额外的 CSS 计算。与其这样做,不如只使用普通的 CSS3 动画。看看这个:http://matthewlein.com/ceaser/

animate() 最好有一个静态值(在这种情况下,我为 data- 属性中的每个立方体存储的原始位置和凸起位置)所以唯一完成的计算是由javascript 解释器本身。

live() 方法会在重新创建多维数据集时自动将事件添加到多维数据集,因此无需在 refreshcubes 函数中设置事件。此外,James Montagne 指出,使用 live() 方法会使整个事情表现得像丹尼尔在他的回答中描述的那样(更快)。 编辑:我通过使用on() 方法而不是live() 使它更快。悬停的事件上下文现在位于每个多维数据集上,而不是整个文档上。有关详细信息,请参阅此问题:How does jQuery's new on() method compare to the live() method in performance?

尽可能创建一个单独的 jquery 对象并用一个变量引用它,这样您就不会每次都重新创建一个新对象。

总的来说,我注意到性能有了很大提高,但仍然比我想象的要慢。也许每次移动立方体(甚至移动一个像素)时都会重新计算 CSS 变换。

编辑:正如 addlovely 建议的那样,修改 jQuery.fx.interval 有助于加快速度。

我认为在 2D 画布或 3D webGL 画布中绘制立方体会更幸运。

尝试使用 three.js 或 processing.js 绘制立方体并为其设置动画。

编辑:我在 Google Chrome 中使用硬件加速 CSS3 动画让它变得更快(其他浏览器仍在开发这些 CSS3 功能,因此它目前仅适用于 Chrome):http://jsfiddle.net/trusktr/JycdN/35/

【讨论】:

  • live() 方法的有趣之处。我一定会实施的。感谢动画优化指针。我有一个用 2D Canvas 编写的,但在达到 15 以上的网格大小后速度也大大降低。还没有涉足 3D webGL 或使用three.js 或 processing.js,但可能很快。
  • WebGL 最酷的地方在于,在视频加速的情况下,一切都会顺利进行。 :D
  • 我刚刚在IE9中测试过。它非常糟糕!
  • 刚刚尝试删除鼠标悬停事件处理程序并添加了 CSS3 转换/转换(translateY),而且它在悬停时响应更慢或响应更慢。
  • 嘿,检查一下,我做得更好了:jsfiddle.net/trusktr/JycdN ..现在动画不会在鼠标每次进入和离开时一遍又一遍地重复。但是,整个动画仍然完全动画。您可能已经注意到,在.animate() 之前添加.stop() 会导致动画停止并且框不会一直弹出。这就是我的意思:jsfiddle.net/trusktr/JycdN/16 ...我认为这几乎和它的速度一样快。
【解决方案2】:

你有 n^2 个听众,真的很惊讶事情和他们一样好。 Jquery 没有足够的信息来优化算法,但我们有。我们实际上只需要检查一次,而不是关心鼠标在哪里 n^2 次。

为整个板添加一个监听器。 On call 计算悬停在哪个单元格上并为该单元格设置动画。将单元格列表保持在“打开”状态并计算鼠标是否仍悬停在它们上方,如果没有将它们设置为原始状态并将它们从列表中删除。

现在,您拥有 1 个帧而不是 n^2 次操作。更多但更快的代码和相同的功能。是的,jquery 很棒,不得不自己做这件事很可惜。请记住,jquery 是为一般网站将使用的 10 位听众设计的,而不是为 100 位或 1000 位听众设计的。

@trusktr:确实更快,但仍然比必要的慢很多倍。

【讨论】:

  • 这很有趣。那么您是说每次移动鼠标时都会激活所有悬停事件,但不一定会采取行动?我从没想过会是这样。另外,你能 fork Ambrosia's 或 My fiddle 并实现你的想法吗?
  • live 甚至更好的delegate 与您所描述的非常相似。它只附加一个侦听器,然后确定其中的匹配元素。
  • @James Montagne 如果是这样,那也许可以解释为什么我的回答要快一些。
  • 有趣,我没想到浏览器会像你说的那样在事件处理程序上遇到这么困难。如果您有时间,我不介意 jsfiddle 叉子,但如果我有时间,我可能会在今晚晚些时候或明天重新阅读您的答案并玩弄它。
【解决方案3】:

除了上面的优点之外,您可能还想查看 Jquery fx Interval。

http://api.jquery.com/jQuery.fx.interval/

默认情况下,动画间隔设置为 13 毫秒,大约每秒 76 帧。看起来很傻,因此将其设置为每秒 20 帧(50 毫秒)以上,将提高性能。

【讨论】:

  • 我测试了你所说的,但没有任何明显的效果。在我的代码中,我只是将大图像向左移动 2000 像素。
猜你喜欢
  • 2017-10-04
  • 2011-10-10
  • 1970-01-01
  • 2017-02-11
  • 2012-07-07
  • 2014-05-17
  • 2012-03-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多