Низкий FPS при прокрутке страницы. Решение проблемы background-attachment: fixed

Katsuro

Проверенные
Сообщения
144
Решения
1
Реакции
47
Баллы
1,730
Инструкция взята с Хабр'а и не является моим, я лишь изменил классы под XF и опубликовал сюда из-за распространённости проблемы: https://habr.com/ru/post/282079/

Решил я тут недавно на одном из своих сайтов сделать легкий редизайн. И дошло дело до фона. Показался он мне каким-то скучным. Захотелось его немного «оживить». Подобрал подходящую картинку небольшого размера, загнал ее в свойство фона:
CSS:
.p-pageWrapper {
    background: url("../images/bg.jpg") no-repeat center center / cover fixed;
}

и довольный нажал F5. Красота, да и только!

Начал скроллить страничку вниз и чувствую, что-то не то…

Сначала я погрешил на свойство cover, но дело оказалось не в нем. Отключив фиксированное положение фона (убрав fixed), мой «Crysis» выдал мне больше 30 FPS! «Во дела...», подумал я. Как же так? Почему? Почему я не замечал этого раньше? Возможно, это не очень заметно на легковесных сайтах, где не так много html элементов.

А дело оказалось вот в чем. Использование background-attachment : fixed каждый раз при прокрутке вызывает операцию перерисовки. Страница должна переместить свое содержимое. И когда дело доходит до фиксированного фона, браузер должен заново прорисовать картинку в новом месте, относительно существующих DOM-элементов.

Чтобы решить эту проблему, нашему фоновому изображению нужен свой элемент, чтобы оно могло двигаться независимо от других. А также нам понадобится CSS3-свойство will-changed. О нем речь пойдет ниже.

Как только мы решим проблему с прорисовкой, скроллинг уже не будет проходить у нас рывками. Так как фон будет лежать на своем собственном слое, больше не потребуется перерисовывать страницу каждый раз при прокрутке.

Давайте я покажу все на примере.

Это наш изначальный код (я развернул свойства для наглядности):
CSS:
.p-pageWrapper {
    background: url("../images/bg.jpg") no-repeat center center;
    background-attachment: fixed;
    background-size: cover;
}

А вот, что нам необходимо сделать для решения проблемы:
CSS:
.p-pageWrapper{
  position: relative;
}

.p-pageWrapper::before {
    background: url("../images/bg.jpg") no-repeat center center;
    background-size: cover;
    content: ' ';
    height: 100%;
    left: 0;
    position: fixed;
    top: 0;
    width: 100%;
    will-change: transform;
    z-index: -1;
}

Мы добавили position: relative для элемента p-pageWrapper , чтобы затем спозиционировать псевдо-элемент, который будет отдельным слоем для нашего фона. Остальные свойства, касательно фона, мы перенесли в ::before. У псевдо-элемента мы теперь используем position: fixed, вместо прежнего background-attachment: fixed у p-pageWrapper . Ну и самое важное, без чего вся затея потерпит крах — это свойство .

Свойство will-change предписывает браузеру отображать элемент, независимо от окружающих его других элементов. Оно как бы говорит браузеру: «Эй, друг, этот элемент изменится когда-нибудь потом, в будущем, так что прорисуй его только один раз на его собственном слое. И не нужно учитывать остальные элементы — он сам по себе».

Такие вот дела.

Данный билд я протестировал в разных браузерах, и вот небольшое резюме:
  1. Google Chrome. Все ОК, работает как часы.
  2. Mozilla Firefox. Все ОК, работает как часы.
  3. Opera. Все ОК, работает как часы.
  4. Safari. Все ОК, работает как часы. За проверку спасибо
  5. Microsoft Edge. Метод работает, но есть один косяк. Если крутить колесиком, то дергается верх и низ страницы, но потом приходят в норму. Если же крутить с помощью скроллбара, то все ОК.
  6. Internet Explorer. Та же проблема, что и у Edge.
 
Последнее редактирование:
Современный облачный хостинг провайдер | Aéza
Назад
Сверху Снизу