为什么我的 CSS3/jQuery 动画的性能这么慢?



为什么,为什么这么慢?我用css3制作了一个等距立方体网格,鼠标悬停时它会上下浮动。它在网格大小小于12的Firefox上运行得很好,在网格大小小于18的Chrome上运行得很好。我有一个不错的显卡和CPU,但困扰我的是,如果我只将鼠标移到一个立方体上,为什么它的动画速度如此之慢。我的JavaScript是否需要优化,或者这只是预期从浏览器CSS3和JavaScript引擎的当前实现?下面的完整测试用例包括滑块,可以随时更改网格大小,您可以自己下载或访问这个由Doug提供的jsfiddle版本。

<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>

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

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

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

live()方法在重新创建多维数据集时自动将事件添加到多维数据集,因此不需要在refreshcubes函数中设置事件。此外,James Montagne指出,使用live()方法使整个事情的行为像Daniel在他的答案中描述的那样(更快)。编辑:我通过使用on()方法而不是live()使它更快。悬停的事件上下文现在在每个多维数据集上,而不是在整个文档上。jQuery' new on()方法与live()方法相比,性能如何?

只要有可能,创建一个jquery对象并使用变量引用它,这样就不必每次都重新创建一个新对象。

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

编辑:正如addedlovely建议的那样,修改jQuery.fx.interval有助于使它更快一些。

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

尝试使用three.js或processing.js绘制立方体并将其动画化。

编辑:我已经使它更快使用硬件加速的CSS3动画在谷歌浏览器(其他浏览器仍在开发这些CSS3的功能,所以它只适用于Chrome现在):http://jsfiddle.net/trusktr/JycdN/35/

你有n^2个监听器,老实说,很惊讶事情能做得这么好。Jquery没有足够的信息来优化算法,但是我们有。而不是关心鼠标的位置n^2次,我们实际上只需要检查一次。

为整个单板添加一个监听器。On call计算哪个单元格被悬停在上面并使其动画化。保持一个单元格列表处于"开启"状态,并计算鼠标是否仍然悬停在它们上面,如果不是,将它们动画到原始状态,并从列表中删除它们。

现在你有1个,而不是n^2个操作。更多,但更快,代码和相同的功能。是的,jquery是很棒的,不得不自己做这个是一个遗憾。请记住,jquery是为一般站点使用的10个侦听器设计的,而不是为100个或1000个侦听器设计的。

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

除了上面的优点,你可能想看看Jquery的fx间隔。

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

默认情况下,动画间隔设置为13ms,大约每秒76帧。看起来很傻,所以把这个设置为每秒20帧(50ms)以上,会给你一个性能提升。

最新更新