Переход с преобразованием CSS - использование "px" более плавное/результативное, чем "процент"

Недавно я изучал улучшение анимации на своем веб-сайте - более конкретно, раскрытие навигации на мобильных устройствах.

В этой связи я наткнулся на следующий случай, о котором я надеюсь получить более глубокие знания.

Дело в том, что при переходе/анимации transform: translate3d() кажется, что браузер требует больше вычислений, когда он применялся с использованием % а не px. Например, в моих тестах кажется, что переход из transform: translate3d(0, 500px, 0) для transform: translate3d(0,0,0) требует меньше вычислений и работает более плавно, чем переход из transform: translate3d(0, 100%, 0),

Обновление: после тестирования я обнаружил, что использование 100vh/100vw обходит /100vw проблему использования процентов. Это может быть usefyk в случаях, когда элемент имеет известную процентную ширину окна или имеет полную ширину и тем самым улучшает производительность. На самом деле кажется, что использование этого значения ведет себя так, как если бы он был присвоен значением px в Chrome.

Вот несколько фотографий временной шкалы из каждой анимации. Временные графики получены с помощью инструментов Google Dev в разделе "Производительность". Чтобы лучше показать разницу, производительность была ограничена в Chrome Dev Tools до "младшего мобильного" (6-кратное снижение производительности процессора).

Преобразование с использованием процентов:

Transform performance using percent (%)

Преобразование с использованием пикселя (px):

Transform performance using pixel (px)

Как видно из изображений, кажется, что при рендеринге и рисовании происходит гораздо больше рендеринга и рисования при использовании % а не px чтобы определить преобразование. Имеет смысл, что браузеру приходится вычислять процентные значения для каждого кадра (я думаю?), Но я удивлен, что это занимает гораздо больше, по сравнению с использованием значения пикселя.

Также обратите внимание, что фреймворки на снимке, показывающие временную шкалу процента, никогда не достигают ~ 60 кадров в секунду, но довольно усредняют около 40 кадров в секунду.

Ниже приведены фрагменты для повторения дела. Там один использует процент и один, используя px.

$(document).on("click", function(){
$(".bb").toggleClass("active");
});
.aa{
  height:50px;
  background:blue;
  position:fixed;
  top:0;
  width:100%;

}
.bb{
  position:fixed;
  top:0px;
  background:none;
  height:100%;
  width:100%;
  left:0;
  transform:translateZ(0);
  overflow:hidden;
    pointer-events:none;
}
.cc{
  height:100%;
    transform:translate3d(0,500px,0);
    width:100%;
      transition:transform .5s ease-in;
      background:red;
}
.bb.active .cc{
  transform:none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Click the document to start animation<p>
<div class="bb">
<div class="cc">
<ul>
<li>Point one</li>
<li>Point two</li>
<li>Point three</li>
<li>Point four</li>
<li>Point five</li>
<li>Point six</li>
<li>Point seven</li>
</ul><ul>
<li>Point one</li>
<li>Point two</li>
<li>Point three</li>
<li>Point four</li>
<li>Point five</li>
<li>Point six</li>
<li>Point seven</li>
</ul>

</div>

</div>

Ответ 1

Правильно ли, что я испытываю такое поведение (присваивание значения в px выполняется лучше, чем%), и если да, то почему это происходит? Как упоминалось ранее, мне кажется, что это необходимо, но мне действительно не хватает какого-то технологического/подробного объяснения.

Это правильно. Пиксели - это абсолютные значения (т.е. Не зависят от чего-либо и представлены "как есть"). Проценты являются относительными значениями, что означает, что они должны зависеть от какого-либо другого значения для получения результата. Поэтому каждый раз, когда вы назначаете процентное значение, он должен получить относительное значение для вычисления. При выполнении перевода с пикселями вам нужно только изменить значения перевода, но с процентами вы должны сначала получить размеры элемента, а затем применить перевод. И это нужно сделать для каждого кадра анимации.

Чтобы смягчить эту проблему, вы должны пересчитать размер элемента только один раз перед анимацией. Затем используйте !important переопределить то, что установлено в атрибуте style. Код в этом фрагменте делает именно это.

Также обратите внимание, что я добавил слушателя resize. Это необходимо, если окно будет изменено, поэтому ваш элемент станет скрытым.

$(function(){
var $el = $("#bb");
$(document).on("click", function(){
  var height = $el.outerHeight();
  $el
    .css('transform', 'translateY(' + height + 'px)')
    .toggleClass("active");
});
$(window).on('resize', function() {
  $el.removeClass('active').removeAttr('style');
});
});
#bb{
  position:fixed;
  top:0px;
  background-color: red;
  height:100%;
  width:100%;
  left:0;
  overflow:hidden;
  transform: translateY(100%);
  transition: transform .5s ease-in;
}
#bb.active
{
  transform: translateY(0px) !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="bb">
<div class="cc">
<ul>
<li>Point one</li>
<li>Point two</li>
<li>Point three</li>
<li>Point four</li>
<li>Point five</li>
<li>Point six</li>
<li>Point seven</li>
</ul><ul>
<li>Point one</li>
<li>Point two</li>
<li>Point three</li>
<li>Point four</li>
<li>Point five</li>
<li>Point six</li>
<li>Point seven</li>
</ul>

</div>

</div>

Ответ 2

Чтобы еще больше расширить ответ, полученный CyperAP, и предложения, сделанные в моем первоначальном вопросе, я также обнаружил, что использование значений CSS vw и vh обходит проблему, вызвав причину %.

Этот прецедент особенно полезен в ситуациях, когда для элемента, подлежащего переходу, была задана высота/ширина на основе размера окна, например, если элемент имеет полную ширину (100%/100vw).

На основе примера из исходного вопроса и вместо этого с помощью transform: translate3d(0, 100vh, 0) дает следующий результат временной шкалы (опять же, в Chrome с производительностью, ограниченной "Low end mobile"):

enter image description here

Фрагмент можно увидеть здесь:

$(document).on("click", function(){
$(".bb").toggleClass("active");
});
.aa{
  height:50px;
  background:blue;
  position:fixed;
  top:0;
  width:100%;

}
.bb{
  position:fixed;
  top:0px;
  background:none;
  height:100%;
  width:100%;
  left:0;
  transform:translateZ(0);
  overflow:hidden;
    pointer-events:none;
}
.cc{
  height:100%;
    transform:translate3d(0,100vh,0);
    width:100%;
      transition:transform .5s ease-in;
      background:red;
}
.bb.active .cc{
  transform:none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Click the document to start animation<p>
<div class="bb">
<div class="cc">
<ul>
<li>Point one</li>
<li>Point two</li>
<li>Point three</li>
<li>Point four</li>
<li>Point five</li>
<li>Point six</li>
<li>Point seven</li>
</ul><ul>
<li>Point one</li>
<li>Point two</li>
<li>Point three</li>
<li>Point four</li>
<li>Point five</li>
<li>Point six</li>
<li>Point seven</li>
</ul>

</div>

</div>