Почему моя анимация CSS3 / jQuery настолько медленная? - PullRequest
12 голосов
/ 05 ноября 2011

Почему это так медленно? Я сделал изометрическую сетку кубов с помощью css3, которая качается вверх и вниз при наведении мыши. Он отлично работает на Firefox с размером сетки менее 12, а с Chrome довольно неплохо работает под 18 лет. У меня приличная видеокарта и процессор, и меня беспокоит то, почему так медленно анимируется анимация только одного куба если я убедлюсь, что наведу указатель мыши только на один куб. Нужно ли оптимизировать мой JavaScript или это просто следовало ожидать от текущей реализации браузерных движков CSS3 и JavaScript? Полный тестовый пример, приведенный ниже, включает в себя слайдер для изменения размера сетки на лету, вы можете скачать его самостоятельно или посетить эту версию 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>

Ответы [ 3 ]

9 голосов
/ 05 ноября 2011

Это быстрее: http://jsfiddle.net/JycdN/1/

Я оптимизировал сам код jQuery. Кроме того, плохая идея анимировать значение CSS в стиле «+ = 25px», потому что каждый раз, когда вы это делаете, вы заставляете браузер выполнять дополнительные вычисления CSS поверх вычислений анимации. Вместо этого вы могли бы просто использовать обычную анимацию CSS3. Проверьте это: http://matthewlein.com/ceaser/

Лучше для animate () иметь статическое значение (в данном случае, исходные и поднятые позиции, которые я сохранил для каждого куба в атрибутах data-), поэтому выполняются только вычисления, выполняемые Сам интерпретатор JavaScript.

Метод live() автоматически добавляет события в кубы всякий раз, когда они воссоздаются, поэтому нет необходимости устанавливать события внутри функции refreshcubes. Также Джеймс Монтань отметил, что использование метода live() заставляет все вести себя так, как Даниил описал в своем ответе (что быстрее). РЕДАКТИРОВАТЬ: Я сделал это еще быстрее, используя метод on() вместо live(). Контекст событий для зависания теперь находится в каждом кубе, а не во всем документе. Подробности смотрите в этом вопросе: Как новый метод on () в jQuery отличается от метода live () в производительности? .

По возможности, создавайте один объект jquery и ссылайтесь на него с помощью переменной, чтобы каждый раз не воссоздавать новый объект.

В целом, я заметил значительное увеличение производительности, но оно все еще медленнее, чем я мог себе представить. Возможно, CSS-преобразования пересчитываются каждый раз, когда кубы перемещаются (даже на пиксель).

РЕДАКТИРОВАТЬ: Как было добавлено ранее, изменение jQuery.fx.interval помогло сделать его немного быстрее.

Думаю, вам больше повезет, если вы будете рисовать кубы в 2D-холсте или в 3D-холсте WebGL.

Попробуйте использовать three.js или processing.js, чтобы нарисовать кубики и анимировать их.

РЕДАКТИРОВАТЬ: я сделал это еще быстрее, используя аппаратно-ускоренную анимацию CSS3 в Google Chrome (другие браузеры все еще разрабатывают эти функции CSS3, поэтому на данный момент она работает только в Chrome): http://jsfiddle.net/trusktr/JycdN/35/

3 голосов
/ 10 ноября 2011

В дополнение к превосходным пунктам, приведенным выше, вы можете проверить интервал Jquery FX.

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

По умолчанию интервалы анимации установлены на 13 мс, около 76 кадров ввторой.Это выглядит глупо, поэтому установка этого значения на более 20 кадров в секунду (50 мс) даст вам повышение производительности.

3 голосов
/ 05 ноября 2011

У вас n ^ 2 слушателей, честно удивленные вещи работают так же, как и они.Jquery не имеет достаточно информации для оптимизации алгоритма, но у нас есть.Вместо того, чтобы заботиться о том, где мышь n ^ 2 раза, нам нужно проверить только один раз.

Добавить слушателя на всю доску.По вызову подсчитайте, какая ячейка находится над вами, и оживите эту.Держите список ячеек в состоянии «включено» и вычислите, находится ли мышь над ними, если не оживите их в исходное состояние и удалите из списка.

Теперь вместо n ^ 2 операцийкадр у вас 1. Больше, но гораздо быстрее, кода и той же функциональности.Да, jquery - это круто, и жалко делать это самостоятельно.Имейте в виду, что jquery предназначен для тех 10-ти слушателей, которые будет использовать средний сайт, а не для 100-х или 1000-х.

@ trusktr: Это действительно быстрее, но все же во много раз медленнее, чем необходимо.

...