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
предписывает браузеру отображать элемент, независимо от окружающих его других элементов. Оно как бы говорит браузеру: «Эй, друг, этот элемент изменится когда-нибудь потом, в будущем, так что прорисуй его только один раз на его собственном слое. И не нужно учитывать остальные элементы — он сам по себе».Такие вот дела.
Данный билд я протестировал в разных браузерах, и вот небольшое резюме:
- Google Chrome. Все ОК, работает как часы.
- Mozilla Firefox. Все ОК, работает как часы.
- Opera. Все ОК, работает как часы.
- Safari. Все ОК, работает как часы. За проверку спасибо smssystem
- Microsoft Edge. Метод работает, но есть один косяк. Если крутить колесиком, то дергается верх и низ страницы, но потом приходят в норму. Если же крутить с помощью скроллбара, то все ОК.
- Internet Explorer. Та же проблема, что и у Edge.
Комментарии (33)
Fedcomp
21.04.2016 09:10+1Всегда знал что фиксированный фон с прозрачными элементами убивает производительность. Спасибо за решение!
istui
21.04.2016 10:09+1Вообще говоря, это косяк браузера, который должен уметь поступать аналогично (не перерисовывая все) сам. Но, к сожалению, пока что приходится ему помогать принять правильное решение.
Спасибо за информацию!anmi
22.04.2016 13:47Спорное утверждение, браузер никому ничего не должен, все оптимизации которые были сделаны, не были каким-то требованием от стандартов. Никто никогда не будет оптимизировать всё «на всякий случай». Если есть желание получить максимум производительности, следует изучить как оптимизации уже сделаны, как браузер на композитные слои разбивает страницу и как рендерит.
smssystem
21.04.2016 10:32+2jsfiddle.net/q8oL13xz/4 — Safari 7.0 работает отлично.
streetflush
21.04.2016 11:43У меня этот пример не тормозит и с
background: url(«http://www.andicbakim.com/wp-content/uploads/2015/12/website-bg.jpg») no-repeat center center / cover fixed;Tomio
21.04.2016 11:54Я работаю дома на ноутбуке HP Pavilion 15, не самой мощной конфигурации. И именно на нем я смог отловить подобное поведение. На работе у меня машинка помощнее, и явно заметных проблем с фиксированным фоном я не испытываю (хотя на серьезных сайтах, все-таки, слегка заметное подвисание не радует глаз). Тут дело, скорее, в GPU. Чем лучше, тем менее заметно.
Попробуйте открыть какой-нибудь сайт по фильмам или сериалам, где обычно в шапке висит реклама фиксированным фоном (например, хдрезка.ме). Покрутите с ним. Потом снимите в DevTools галку с fixed и посмотрите разницу =) Если разницы не видно, то я вам искренне завидую =)streetflush
21.04.2016 12:41Да, на хдрезка.ме лаги заметны, но там тормозит не только из за фона =). При отключении, тормоза уменьшились, но остались.
Видео на кристалле i5
chikuyonok
21.04.2016 11:38+3Свойство will-change предписывает браузеру отображать элемент, независимо от окружающих его других элементов.
Не совсем так. Оно ничего не предписывает, а только подсказывает браузеру, как может в дальнейшем меняться этот элемент, а браузер, исходя из окружения и собственных оптимизаций может принять решение вынести его на отдельный композитный слой. А может и не вынести, оставив его частью другого композитного слоя. А может вообще ничего не делать.
В целом у этого решения (принудительный вынос всего подряд на отдельный композитный слой) есть очень много побочных эффектов: как минимум увеличенное потребление памяти, как максимум — repaint всего слоя на, казалось бы, банальных вещах вроде изменения цвета ссылки внутри слоя по ховеру. Так что следует пользоваться этим аккуратно и следить в DevTools/Web Inspector за аномальным перерисовками.
Bhudh
21.04.2016 11:43В общем-то любой неподвижный элемент на странице снижает FPS. Просто на современных машинах это в основном незаметно.
Например, на старой машине хабр будет тормозить из-за левой панели с position: absolute;.
psvg42
21.04.2016 11:44Про will-change свойство, написано не совсем правильно, насколько я понимаю, will-change: transform; подсказывает браузеру, что элемент будет двигаться, и ему стоит подготовить для его отрисовки отдельный слой. И мне не совсем понятно, почему в параметрах указан именно transform, ведь по идее как раз transfrom не происходит? Хоть могу ошибаться, и тогда получается, что браузер для скрола использует transform?
Что если в качестве параметра для will-change указать opacity к примеру?
daggert
21.04.2016 11:44+2Я схватился за голову и рву себе волосы…
Лет десять назад, я делал сайты под IE6. Дизайн и верстка были ужасны, но, практически везде на них я юзал fixed background, который я слизал с руководства по CS:Source. Я делал пачку сайтов именно с тем кодом что предложил автор в начале статьи и ни разу не испытывал проблем. Машины были слабые в то время, 256 мегабайт оперативки и процессор на 1.8 гигагерц.
Вопрос: Почему?! Почему ранее это работало идеально и не вызывало проседание FPS?ExplosiveZ
21.04.2016 11:56-1Было меньше HTML элементов, картинки были в несколько раз меньше.
daggert
21.04.2016 11:59Да я не сказал-бы что сейчас все сложнее стало в разы. Вспомнить старые сайты на таблицах, с кучей инлайн стилей…
Tomio
21.04.2016 11:58+2Динамика сайтов сейчас колоссальная! UX-дизайнеры порой воротят таких монстров, что хоть стой, хоть падай. Плюс накручено большое количество скриптов, которые в реальном времени постоянно отслеживают и меняют то или иное поведение элементов на странице. Раньше сайты были проще, и трава зеленее =)
anmi
22.04.2016 13:55Во всех браузерах поменялась модель рендеринга, они оптимизирована под работу с видеокартой. Посчитали лэйаут, отрендерили по тайлам, залили текстуры в видеокарту. Постоянно перегенерировать тайл просто дорого, тогда как раньше перерендеривалось всё постоянно, но без возможности масштабироваться на большие разрешения, в том числе и для картинок с бэкграунда.
Aingis
25.04.2016 15:05Лет десять назад, я делал сайты под IE6. Дизайн и верстка были ужасны, но, практически везде на них я юзал fixed background, который я слизал с руководства по CS:Source.
Очень интересно, что вы это пишете, так как IE6 на самом деле не поддерживает background-attachmend: fixed. Точнее само свойство поддерживалось, но картинка скроллилась как обычный фон. Пруф: http://www.quirksmode.org/css/backgroundattachment.html
Это обходилось либо с помощью экспрешнов (тормозная штука по своей природе, javascript-в-css) или с помощью position: absolute и прокруткой только контента.daggert
25.04.2016 21:56Я даже напрягся и вспомнил более детально!
Помните в win98 была такая штука — отображение папки как web-страницы? У меня оттуда перекочевала папка с сформированным html документом для отображения. С папки windows была скопирована функция «изменение содержимого данной папки бла-бла-бла», а с руководства по Counter Strike: Source я скопировал background-attachment свойство. Данная папка отображалась у меня через дефолтный windows explorer, а следовательно через IE6?
Далее я вспоминаю о первом сайте для школы, где это свойство использовалось для фотоальбома. Основной браузер в ТЗ (бумажка была даже) был IE6.
За замечание спасибо. Я даже в замешательстве теперь о своей памяти…
xargon
21.04.2016 12:26Я бы дополнительно порекомендовал применять фон к HTML, как корневому элементу, что дополнительно «разгрузит» body.
kirillunlimited
21.04.2016 15:54В свое время столкнулся с этой проблемой при написании своего параллакса.
Несмотря на то, что Вы упомянули, что в самом начале пробовали избавиться от «background-size: cover» и не получили выигрыша в производительности, в моей ситуации отказ от этого свойства решил проблему.
Krakabek
21.04.2016 18:29К слову, position: relative у body не обязательно указывать, так как position:fixed позиционируется относительно окна браузера (или относительно родителя с transform: translateZ, но это совсем другой разговор)
NeXTs_od
25.04.2016 16:46или относительно родителя с transform: translateZ
а расскажи подробнее, что ты имел ввиду?Krakabek
25.04.2016 17:343d трансформации (такие как transform: translateZ(0) или же transform: translate3D(0, 0, 0)) вызывают некоторые побочные эффекты:
Для рендера подключается hardware accelleration, и это чинит некоторые баги с транзишенами или просто когда в сафари часть сайта не отрисовывается.
Создается новый «position scope» (сам придумал, буду признателен, если кто-то подскажет, как это назвать по-человечески) и для дочерних элементов с position: absolute/fixed позиционирование происходит относительно родителя с 3d трансформацией.NeXTs_od
25.04.2016 17:44Про position:fixed относительно родителя не верю :)
Докажешь codepen'ом?
Я вот только после твоего комментария сегодня узнал что браузеры не позволяют одновременно использовать position:fixed, когда у родителя стоит transform: translateZ(0). position: fixed элемент при этом ведёт себя как relative. А ты тут говоришь такое)
Вот смотри, у меня в хроме на вин7, например, не работает.
Баг в хромиуме тянется с 2009 года, который до сих пор не починили. Вон тут некий Эрик разжевывал это ещё в 2011 году.Krakabek
25.04.2016 17:53Хех, и правда, и себя запутал, и тебя.
Имел ввиду поведение как position: relative.
Спасибо :)
imhvost
22.04.2016 13:09Так-то оно так, только что делать, когда background-attachment:fixed нужно прикрутить к нескольким блокам, а не только к body?
AlAyres
24.04.2016 11:58Отличное решение, но есть одна неточность.
Позиционирование родительского элемента, когда мы используем position: fixed абсолютно бесполезно. Так как fixed не привязывается к родительским элементам.
Это нас приводит к следующей проблеме — при необходимости сверстать более одного блока с разным фиксированным фоном для каждого из блоков, то последний добавленный фон будет перекрывать все добавленные ранее.
В общем, проблемой это становится только в случае, если имеется более одного изображения.
NeXTs_od
25.04.2016 08:44Указали бы что это перевод скринкаста Пола Льюиса :)
https://www.youtube.com/watch?v=QU1JAW5LRKU
Ronnie_Gardocki
28.04.2016 09:31Целая статья посвященная промоутингу элементов в отдельный слой для композитинга? Это ведь по сути web-performance 101, все это можно узнать из любой толковой статьи или курса, в котором объясняют про особенности рендеринга в браузерах и учат как юзать dev tools для профилирования.
swpo
Выложи свой код в репозиторий и на сафари, тогда посмотрю. Да я немного ленив :)