Почему моя производительность анимации CSS3/jQuery настолько медленная?
Почему, почему это так медленно? Я сделал изометрическую сетку кубов с 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="#" onclick="location.href='http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css'; return false;" />
<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>
Ответы
Ответ 1
Это быстрее: http://jsfiddle.net/JycdN/1/
Я оптимизировал сам код jQuery. Кроме того, его плохая идея оживить значение CSS стиля "+ = 25px", потому что каждый раз, когда вы делаете то, что вы заставляете браузер делать дополнительные вычисления CSS в дополнение к вычислениям анимации. Вместо этого вы можете просто использовать простые анимации CSS3. Проверьте это: http://matthewlein.com/ceaser/
Лучше для animate() иметь статическое значение (в этом случае исходные и поднятые позиции, которые я сохранил для каждого куба в атрибутах data-
), поэтому единственными выполненными вычислениями являются сам интерпретатор javascript.
Метод live()
автоматически добавляет события в кубы всякий раз, когда они воссозданы, поэтому нет необходимости устанавливать события внутри функции refreshcubes. Кроме того, Джеймс Монтань отметил, что использование метода live()
приводит к тому, что все это ведет себя так, как Даниэль описал в своем ответе (что быстрее).
EDIT: Я сделал это еще быстрее, используя метод on()
вместо live()
. Контекст события для зависания теперь находится на каждом кубе, а не на всем документе. См. Этот вопрос для деталей: Как новый метод on() jQuery сравнивается с методом live() в производительности?.
По возможности сделайте один объект jquery и ссылайтесь на него с переменной, таким образом вы не будете каждый раз воссоздавать новый объект.
В целом, я заметил довольно значительное увеличение производительности, но все еще медленнее, чем я мог себе представить. Возможно, преобразования CSS пересчитываются каждый раз, когда кубы перемещаются (даже пикселем).
EDIT: Как было добавлено, изменение jQuery.fx.interval помогло сделать это немного быстрее.
Я думаю, вам будет больше удачи рисовать кубы в двумерном холсте или с холстом 3D webGL.
Попробуйте использовать three.js или processing.js для рисования кубов и анимации.
EDIT: я сделал это еще быстрее, используя аппаратные ускорения CSS3-анимации в Google Chrome (другие браузеры все еще разрабатывают эти функции CSS3, поэтому он работает только в CHrome на данный момент): http://jsfiddle.net/trusktr/JycdN/35/
Ответ 2
У вас есть n ^ 2 слушателя, честно удивленные вещи работают так же хорошо, как и они. Jquery не располагает достаточной информацией для оптимизации алгоритма, но мы это делаем. Вместо того, чтобы заботиться о том, где мышь n ^ 2 раза, нам действительно нужно только один раз проверить.
Добавить слушателя на всю доску. По вызову вычислить, какая ячейка зависнет и оживить ее. Сохраните список ячеек в состоянии 'on' и подсчитайте, продолжает ли мышь курсировать над ними, если они не оживляют их исходное состояние и не удаляют их из списка.
Теперь вместо n ^ 2 операций у вас есть кадр 1. Больше, но гораздо быстрее, код и те же функции. Да, jquery - это потрясающе, и сделать это самому очень жаль. Имейте в виду, что этот jquery предназначен для тех 10 слушателей, которые будут использовать средний сайт, а не 100 или 1000.
@trusktr: это действительно быстрее, но все же во много раз медленнее, чем необходимо.
Ответ 3
В дополнение к превосходным точкам выше вы можете проверить интервал JQuery fx.
http://api.jquery.com/jQuery.fx.interval/
По умолчанию интервалы анимации устанавливаются на 13 мс, около 76 кадров в секунду. Кажется глупым, поэтому установка этого параметра на более 20 кадров в секунду (50 мс) даст вам повышение производительности.