Для мобильных одностраничных веб-приложений для достижения эффекта, сравнимого с нативными приложениями, необходима анимация перехода между страницами. Общие анимации перехода страниц включают в себя:
- Shift — текущая страница перемещается из видимой области по горизонтали влево или вправо, а следующая страница перемещается в видимую область с противоположного направления.
- Изменение непрозрачности - текущая страница исчезает, следующая страница появляется.
- 1 и 2 выполняются одновременно.
(Примечание: следующее обсуждение и экспериментыChrome68 браузерная среда)
Частота обновления экрана большинства современных устройств составляет60 раз/сек, бюджетное время на кадр составляет около 16,66 миллисекунд (1/60 секунды). Учитывая, что у браузера есть другая работа, время бюджета на самом деле составляет всего лишь10 мс. Чем больше разница с этим бюджетным временем, тем более застрявшим будет процесс анимации для пользователя. Итак, что будет сделано за эти 10 миллисекунд? При использовании JavaScript для достижения эффектов визуального взаимодействия обычно выполняется следующий процесс:
- JavaScriptисполнение. Например, изменить стиль элемента или добавить/удалить классы стиля для элемента.
- расчет стиля. Вычисляет окончательный стиль элемента в соответствии с правилами стиля.
- макет(макет). Основываясь на результатах предыдущего шага, рассчитайте размер пространства, занятого элементом, и его положение на экране. Обратите внимание, что изменение макета одного элемента может вызвать связанное изменение в других элементах.
- рисовать(краска). Процесс заполнения пикселей, включая каждую видимую часть элемента. Как правило, рисунок выполняется на нескольких слоях.
- синтез(составной). Объедините слои в правильном порядке в один слой и отобразите его на экране.
Стоит отметить, что не каждый кадр проходит каждый из вышеперечисленных шагов. Если геометрические свойства элемента (размер, положение) не изменяются, то макет не требуется, если даже внешний вид элемента не меняется, чертеж не требуется. Таким образом, ключом к достижению плавной анимации являетсяКак уменьшить макет и нарисовать.
смещение
Для анимации смещения наиболее прямым способом является установка абсолютного позиционирования элемента, а затем изменение его левого значения стиля. Например:
<!DOCTYPE html>
<html>
<head>
<style>
.page {
position: absolute;
left: 0;
top: 0;
width: 100%;
min-height: 100%;
background: #ddd;
transition-duration: 2s;
transition-property: left;
}
.leave {
left: -100%;
}
</style>
</head>
<body>
<div id="page" class="page"></div>
<script>
var page = document.getElementById('page');
setTimeout(function() {
page.classList.add('leave');
}, 2000);
</script>
</body>
</html>
Используйте панель «Производительность» в инструментах разработчика Chrome для записи журнала производительности процесса анимации, как показано на следующем рисунке:
Видно, элемент находится в процессе перемещенияПостоянно запускает компоновку и отрисовку. Поэтому производительность этой реализации крайне низкая. Много литературы в Интернете порекомендуетtransformСмена левого заменяет смену левого, а какова реальная ситуация? Немного измените код стиля:
.page {
position: absolute;
left: 0;
top: 0;
width: 100%;
min-height: 100%;
background: #ddd;
transition-duration: 2s;
transition-property: transform;
}
.leave {
transform: translateX(-100%);
}
Журнал производительности записи показан на следующем рисунке:
Видно, что отрисовка срабатывает только в начале и конце анимации, а макет вообще не срабатывает. В результате производительность значительно улучшилась. Однако здесь есть еще два вопроса:
- Почему процесс анимации преобразования не запускает макет и рисование?
- Почему две отрисовки запускаются до начала анимации и одна после окончания анимации?
Чтобы ответить на эти два вопроса, необходимо понять слой композиции.
синтетический слой
Когда определенные условия выполнены, элемент будет назначен отдельному слою для рендеринга. До тех пор, пока содержимое слоя не изменяется, рисунок не будет срабатывать, а браузер напрямую сформирует новый кадр через синтез. Общие условия для продвижения в Синтетические слои включают в себя:
- Анимация или переход применяются к непрозрачности или преобразованию;
- имеет 3D-преобразование;
- will-change устанавливается на непрозрачность или преобразование.
Очевидно, что анимация преобразования смещения в предыдущем разделе удовлетворяет первому условию. Итак, процесс рендеринга всей анимации выглядит так:
- В начале анимации, так как div.page продвигается в самостоятельный составной слой, его необходимо перерисовать, а слой, на котором находится документ, эквивалентен отсутствующему фрагменту контента и должен быть перерисован;
- В процессе анимации div.page не претерпевает других изменений, поэтому верстка и отрисовка не запускаются;
- После окончания анимации div.page перестает быть независимым составным слоем и возвращается к слою, где находится документ, поэтому документ рисуется снова.
Если вы разрешите div.page всегда отображать в отдельном слое композиции, вы можете сохранить ссылку на рисунок в описанном выше процессе. Добавьте «will-change: transform» в код стиля:
.page {
position: absolute;
left: 0;
top: 0;
width: 100%;
min-height: 100%;
background: #ddd;
transition-duration: 2s;
transition-property: transform;
will-change: transform;
}
Журнал производительности записи выглядит следующим образом:
Видно, что шага рисования нет.
Кстати, в инструментах разработчика Chrome есть панель «Слои», которая позволяет легко увидеть, какие слои объединены на странице и почему они объединены.
(Примечание. Так как более ранние версии браузеров не поддерживают волю-изменение, в практических приложениях, если вы хотите обновить элемент до отдельного слоя композиции для рендеринга, вы можете использовать «transform: translateZ(0)»)
Непрозрачность
Как мы все знаем, непрозрачность контролируется стилем непрозрачности. Так будет ли изменение непрозрачности запускать компоновку и рисование? Измените код стиля следующим образом:
.page {
position: absolute;
left: 0;
top: 0;
width: 100%;
min-height: 100%;
background: #ddd;
transition-duration: 2s;
transition-property: opacity;
}
.leave {
opacity: 0;
}
Журнал производительности записи показан на следующем рисунке:
В обычном распознавании изменения непрозрачности не вызывают изменений в положении и размере элементов и не должны запускать компоновку. Однако описанный выше процесс запускал компоновку, и производительность была довольно странной. Затем добавьте «will-change: opacity» в div.page, чтобы он всегда отображался в отдельном композитном слое. Журнал производительности записи выглядит следующим образом:
Видно, что розыгрыш все равно сработает. Для этого «одноразового макета» и «одноразового рисунка» я провел дальнейшие эксперименты и пришел к следующим выводам:Когда непрозрачность изменяется с 1 (включая случай, когда она не установлена, то же самое ниже) до менее 1 и от менее 1 до 1, макет и рисование будут запущены; даже рендеринг в отдельном композитном слое может сохранить только макет, а не рисование.
Поскольку во время анимации непрозрачности происходит только одно изменение с 1 на менее 1, приведенный выше макет и рисунок запускаются только один раз.
Смещение и непрозрачность
Используйте две анимации одновременно, измените код стиля следующим образом:
.page {
position: absolute;
left: 0;
top: 0;
width: 100%;
min-height: 100%;
background: #ddd;
transition-duration: 2s;
transition-property: transform, opacity;
}
.leave {
transform: translateX(-100%);
opacity: 0;
}
Как описано выше, процесс анимации запустится:
- Макет, срабатывающий в начале анимации, вызванный непрозрачностью;
- Две отрисовки, срабатывающие в начале анимации, вызванные непрозрачностью и продвижением на отдельный слой композитинга;
- Независимый синтетический слой возвращается к слою документа.
Если добавить «will-change: transform, opacity», чтобы div.page всегда отображался в отдельном слое композиции, запускается только один рисунок, вызванный непрозрачностью.
Однако создание нового композитного слоя не бесплатно, это приводит к дополнительным затратам.накладные расходы памяти. В одностраничном приложении элемент, применяющий анимацию перехода между страницами, является самым внешним контейнером страницы, который содержит всю структуру содержимого страницы. Если вы позволите ему рендерить в отдельном композитном слое в течение длительного времени, потребление памяти будет очень большим.
Таким образом, он может быть отрендерен в отдельном композитном слое только во время анимации, оставаясь при этом нормальным в остальном.
Конфликт между преобразованием и фиксированным
Если вы используете преобразование для анимации перехода между страницами, вы, должно быть, столкнулись с проблемой: положение фиксированных элементов на странице стало ненормальным.
Следующий код имитирует процесс входа на страницу, чтобы продемонстрировать эту проблему:
<!DOCTYPE html>
<html>
<head>
<style>
.page {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 150%;
background: #ddd;
transition-duration: 3s;
transition-timing-function: cubic-bezier(.55, 0, .1, 1);
transition-property: transform, opacity;
}
.before-enter {
transform: translateX(100%);
opacity: 0;
}
.fixed {
position: fixed;
right: 0;
bottom: 0;
width: 100%;
height: 160px;
background: #ffc100;
}
</style>
</head>
<body>
<div id="page" class="page before-enter">
<div class="fixed"></div>
</div>
<script>
var page = document.getElementById('page');
setTimeout(() => {
page.classList.remove('before-enter');
}, 2000);
</script>
</body>
</html>
Эффект операции следующий:
Как видите, желтый элемент с фиксированным положением появляется внезапно после окончания анимации. Куда оно девалось до этого?
Если вы установите стиль «transform» или «will-change: transform» для любого предка элемента с фиксированным положением, то элемент будет позиционирован относительно ближайшего предка с указанным выше набором стилей.
Поскольку высота div.page установлена на 150%, во время процесса анимации желтый элемент фактически перемещается вниз страницы (за пределы видимого диапазона браузера). В некоторых старых мобильных браузерах (таких как Safari в iOS 9) проблема более серьезная, и элементы с фиксированным положением могут исчезнуть и никогда больше не появляться.
В сети доступны два решения:
- Моделируйте фиксированное позиционирование с абсолютным позиционированием. Хотя это возможно, в мобильных браузерах будут некоторые детали взаимодействия, и прокрутка внутри элемента может легко конфликтовать с прокруткой страницы.
- Поместите элемент с фиксированным положением за пределы элемента, к которому применяется анимация преобразования. Однако это менее осуществимо для одностраничных приложений, разработанных с использованием таких фреймворков, как «Vue.js», потому что в таких фреймворках страница является компонентом, и отделить элемент от страницы сложнее, хлопотно.
Итак, вот третий вариант -После окончания анимации перехода страницы(На данный момент стиль преобразования был удален и больше не влияет на исправление),Затем позвольте элементу с фиксированным позиционированием быть вставленным в контейнер страницы.. И, чтобы сделать это менее резким, добавьте анимацию плавности. Основные пункты модификации кода следующие:
@keyframes kf-move-in {
0% { transform: translateY(100%); }
100% { transform: translateY(0); }
}
.move-in {
animation-name: kf-move-in;
animation-duration: 0.45s;
}
<div id="page" class="page before-enter"></div>
<script>
var page = document.getElementById('page');
setTimeout(function() {
// 监听过渡结束
page.addEventListener('transitionend', function() {
// 创建、插入固定定位元素
var div = document.createElement('div');
div.className = 'fixed move-in';
page.appendChild(div);
});
page.classList.remove('before-enter');
}, 2000);
</script>
Эффект операции следующий:
Таким образом, все взаимодействие становится более дружелюбным. Это также показывает, что технические проблемы не обязательно решаются только с помощью технологии, но также могут быть решены посредством взаимодействия.
использованная литература
- «Производительность рендеринга»
- «Придерживайтесь только атрибутов синтезатора и счетчиков управления»
- «Оптимизация производительности беспроводной сети: композит»
Эта статья также опубликована в личном блоге автораМуронг Луо.life/статья/Порядочность….