Низький FPS при прокручуванні сторінки. Рішення проблеми background-attachment: fixed

Вирішив я тут недавно на одному з своїх сайтів зробити легкий редизайн. І дійшла справа до тла. Здався він мені якимось нудним. Захотілося його трохи «оживити». Підібрав відповідну картинку невеликого розміру, загнав її в властивість фону:

body{
background: url("../images/bg.jpg") no-repeat center center / cover fixed;
}


і задоволений натиснув F5. Краса, та й годі!

Почав скролл сторінку вниз і відчуваю, що щось не те…


Таке відчуття, ніби я граю в Crysis на дуже старому комп'ютері. Чому ж на сайті почалися «гальма» і прокрутка відбувається ривками?

Я почав своє розслідування…


Спочатку я зблудив на властивість
cover
, але справа не в ньому. Відключивши фіксоване положення фону (прибравши fixed), мій «Crysis» видав мені більше 30 FPS! «В матеріалах справи...», подумав я. Як же так? Чому? Чому я не помічав цього раніше? Можливо, це не дуже помітно на легковагих сайтах, де не так багато html-елементів.

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

Щоб вирішити цю проблему, нашому фонового зображення потрібен свій елемент, щоб воно могло рухатися незалежно від інших. А також нам знадобиться CSS3-властивість
will-change
. Про нього мова піде нижче.

Як тільки ми вирішимо проблему з промальовуванням, скролінг вже не буде проходити у нас ривками. Так як фон буде лежати на своєму власному шарі, більше не потрібно перемальовувати сторінку кожен раз при прокручуванні.

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

Це наш початковий код (я розгорнув властивості для наочності):

body{
background: url("../images/bg.jpg") no-repeat center center;
background-attachment: fixed;
background-size: cover;
}


А ось, що нам необхідно зробити для вирішення проблеми:

body{
position: relative;
}
body::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
для елемента
body
, щоб потім поставити псевдо-елемент, який буде окремим шаром для нашого фону. Інші властивості, відносно фону, ми перенесли в
::before
. У псевдо-елемента ми тепер використовуємо
position : fixed
, замість колишнього
background-attachment: fixed
body
. Ну і найважливіше, без чого вся затія потерпить крах, — це властивість will-change.

Властивість
will-change
наказує браузеру відображати елемент, незалежно від навколишніх його інших елементів. Воно як би говорить браузеру: «Ей, друже, цей елемент зміниться коли-небудь потім, в майбутньому, так що промалюй його тільки один раз на його власному шарі. І не потрібно враховувати інші елементи — він сам по собі».

Такі ось справи.

Цей білд я протестував в різних браузерах, і ось невелике резюме:

  1. Google Chrome. Все ОК, працює як годинник.
  2. Mozilla Firefox. Все ОК, працює як годинник.
  3. Opera. Все ОК, працює як годинник.
  4. Microsoft Edge. Метод працює, але є один косяк. Якщо крутити коліщатко, то смикається верх і низ сторінки, але потім приходять у норму. Якщо ж крутити з допомогою скроллбара, то все ОК.
  5. Internet Explorer. Та ж проблема, що і у Edge.


В Safari, на жаль, немає можливості перевірити. Але, по ідеї, все повинно працювати без проблем. Буду вдячний, якщо хтось протестує і напише результат в коментарях.

Джерело: Хабрахабр
  • avatar
  • 0

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.