Pull to refresh
211
0
Сергей Чикуёнок @chikuyonok

Фронт-энд разработчик

Send message

Насколько я знаю, GreenShock/GSAP это больше про разные сложные анимации, а не про оптимизации. Статья в первую очередь о том, что даже за простой CSS-анимацией скрывается сложный механизм, который может привести к проблемам. И понимание того, как это работает, поможет этих проблем избежать: банально поднять анимацию по z-index или перенести в другое место страницы, чтобы не создавались отдельные слои

Тут больше важно не количество слоёв, а их размер. Но и увеличивать количество слоёв лишний раз не стоит. Например, браузер может по своей внутренней логике прекратить предварительный перенос на композитный слой элементов с will-change, если слоёв станет слишком много. Chrome может по той же причине может включить Layer Squashing, с которым мне приходилось бороться, так как оптимизированный вариант занимал слишком много памяти. Каких-то конкретных цифр назвать не могу.

Можно сделать без лишних классов и @keyframe-секций ;)

SVG в любом случае растеризуется, превращается в набор пикселей. Так что не важно, какого размера у вас векторное описание, важно в каком размере вы выводите изображение на экран (см. пример оптимизации)

Каждый слой — это отдельное изображение, размер которого и отображается. Вы можете написать предложения с улучшениями разработчикам Chrome DevTools ;)

Судя по скриншотам, вы в обоих случаях смотрите размеры рутового слоя. Раскройте слева секцию document и найдите там нужный слой

Для картинки без разницы, что там нарисовано: тень или что-то ещё. Это просто набор пикселей.


Можете пример показать, где при изменении размера блока не меняется расход памяти?

Объём занятых ресурсов отображается в панели Layers, по ней и мониторю. В Хроме ещё есть chrome://tracing/, но там тяжелее мониторить, но вроде как более достоверная информация

Честно признаюсь: так глубоко не влезал. Но из найденной информации вроде как возможность компрессии текстур зависит от поддерживаемых OpenGL расширений и эта поддержка зависит от конкретного вендора GPU. Да и и при общении с разработчиками Chrome DevTools пару лет назад они обмолвились, что вроде как слои без компрессии хранятся.

Свойство will-change предписывает браузеру отображать элемент, независимо от окружающих его других элементов.

Не совсем так. Оно ничего не предписывает, а только подсказывает браузеру, как может в дальнейшем меняться этот элемент, а браузер, исходя из окружения и собственных оптимизаций может принять решение вынести его на отдельный композитный слой. А может и не вынести, оставив его частью другого композитного слоя. А может вообще ничего не делать.


В целом у этого решения (принудительный вынос всего подряд на отдельный композитный слой) есть очень много побочных эффектов: как минимум увеличенное потребление памяти, как максимум — repaint всего слоя на, казалось бы, банальных вещах вроде изменения цвета ссылки внутри слоя по ховеру. Так что следует пользоваться этим аккуратно и следить в DevTools/Web Inspector за аномальным перерисовками.

Вроде через chrome://tracing/ это можно отловить, хотя не так удобно, как через Timeline
Да, есть такое свойство. С помощью которого вы всего лишь говорите браузеру «я планирую менять вот такие свойства, оптимизируй это как-нибудь». А как браузер это оптимизирует — вынесет отдельным слоем на GPU, склеит с другими блоками и вынесет отдельным слоем или вообще оставит на основном холсте — вы не знаете. Вы не можете с помощью этого свойства сказать «сразу вынеси этот слой на GPU, потому что потом я буду анимировать кучу блоков и мне не нужен лишний repaint перед стартом анимации; я даю отчёт всем своим действиям и принимаю на себя все возможные риски». Я очень долго на последнем визульано-сложном проекте искал возможность сделать именно так, и никакой will-change тут не помогал. Я пытался именно обойти браузерную оптимизацию и контролировать весь процесс самостоятельно для достижения нужного соотношения производительности и расхода памяти.
У данного подхода, тем не менее, есть отрицательная сторона.

Их гораздо больше :) И если их не учитывать — получим обратный эффект: сильное снижение производительности.

  1. Отдельный GPU-слой почти всегда занимает отдельную память, которую можно посчитать (в байтах) по довольно приблизительной формуле width × height × 4. То есть чем больше слоёв (и чем больше физические размеры слоя), тем больше расход памяти. Неумелым использованием слоёв можно очень быстро вырубить браузер на мобилках.
  2. В Webkit-браузерах (десктоп и мобильные) слой перерисовывается целиком. Это значит, что если где-то внутри слоя поменяется хотя бы один пиксель, браузер сделает repaint всего слоя, а также потратит время на перенос его в GPU. Хотя Blink и научился оптимизировать такие вещи, за этим всё равно нужно следить с помощью описанных в статье инструментов.
  3. Если GPU-слой по z-index будет ниже, то и этот слой тоже будет вынесен на GPU. Со всеми последствиям, описанными выше и с возможными артефактами отрисовки.


Вообще стоит понимать, что GPU-слои (или compositing, как это называется в Blink) — это один большой хак, который сами разработчики браузеров пытаются от нас скрыть и заставить браузер работать так, будто никаких GPU и не-GPU слоёв нет. Поэтому нет никакого «официального» описания как этим правильно пользоваться. Более того, поведение браузеров постоянно меняется: например, Blink далеко не всегда принудительно вынесет слой на GPU даже если ему указать transform: translateZ(0), так он пытается оптимизировать работу с памятью и железом. Иногда это к лучшему, а иногда приходится искать варианты обхода этой оптимизации, чтобы браузер не делал лишний repaint перед анимацией.

Поэтому когда собираетесь выносить что-то на GPU, нужно чётко представлять себе все возможные последствия и уметь правильно пользоваться инструментами для отладки. Я таким образом уже несколько сайтов спас от крэшей на мобильных устройствах :)
четвертый вариант — убрать пересечние блока с текстом и блока с анимацией?

Нет, пересечения тут ни при чем, вынос происходит именно по z-стэку. Потому что браузер не может знать заранее, будут ли блоки пересекаться, если вы, например, анимируете их через JS.
… я увидел у слайда CSS свойство background-size: cover. Это свойство растягивает картинку на всю площадь блока… Ресайз картинки — это очень дорогостоящая операция, поэтому я отключил это CSS свойство и все стало совсем хорошо.


Ресайз картинки — дорогая операция, но делается один раз на repaint. Поэтому проблема с производительностью не в других размерах у картинки, а в постоянной перерисовке блока. А это, скорее всего, из-за неправильной композиции.
Скорее всего вы видели что-то типа этого: files.chikuyonok.ru/anim-demo/

И в большинстве случаев причина — неправильная композиция слоёв. В данном примере слой .anim переносится на GPU для ускорения анимации. То есть для этого слоя создаётся отдельная 3D-плоскость со своей текстурой, которая и компонуется с остальной страницей (которая, к слову, тоже является своего рода 3D-плоскостью). Однако слой .text по z-index находится выше слоя .anim, и браузеру ничего другого не остаётся как вынести слой .text также на отдельную плоскость в GPU, чтобы на экране вы увидели именно то, что задумали.

Соответственно, этот вынос слоя с текстом на GPU (вернее, особенность Safari отрисовки текста на прозначном фоне для GPU-слоя) и даёт тот самый артефакт, Обратите внимание, что этот артефакт появляется строго в начале и в конце анимации, то есть когда меняется 3D-композиция.

Решений этой проблемы несколько:

  1. Перенести слой .text по z-index ниже слоя .anim, если дизайн это позволяет. В этом случае не будет происходить неявный вынос слоёв в 3D. Это самое правильное и оптимальное решение.
  2. Указать слою .text непрозрачный фон, например, белый. Так вы замаскируете артефакт, но не избавитесь от проблемы неявного выноса. А это именно проблема, так как вынос на GPU – это всегда отдельный repaint и отдельная память.
  3. Явно вынести слой .text на GPU, хоть тем же translateZ(0). Так вы сделаете артефакт постоянным, но он хотя бы не будет «моргать». И вы также не избавитесь от проблемы.


Вообще, прежде чем выносить что-то на GPU нужно хорошо представлять себе, что это такое и как именно это работает в недрах системы. Не стоит полагаться, что добавление «волшебного» translateZ(0) также «волшебно» ускорит страницу. Да, анимация будет быстрее, однако при неправильной композиции вы рискуете получить кучу проблем на скролле страницы или на банальном ховере на ссылках.
Спасибо за интересный обзор. В свою очередь хотел бы отметить следующее.

В целом «притормаживание скролла» указывает на другую проблему, а именно композиция слоёв (layer compositing). Так как сегодня практически все браузеры используют GPU для отрисовки некоторых вещей, а отовсюду слышится «ставь translateZ(0) чтобы быстро и плавно всё» эта проблема становится очень актуальной.

Если коротко, то в браузерах (как минимум на базе WebKit и Blink) есть набор триггеров, которые переносят некоторые слои на отдельную поверхность на GPU (в терминах Blink это перенос RenderLayer на свой GraphicsLayer). Эти триггеры могут быть как явно указаными у слоя (тот же translateZ(0), CSS-анимации на transform и opacity), так и косвенными, например, не-GPU слой по z-index’у находится выше GPU-слоя и их границы пересекаются.

Вынос на GPU, помимо каких-то внутренних процессов, как правило сопровождается полной перерисовкой слоя, поэтому иногда можно наблюдать всякие «моргания» и прочие артефакты. На GPU такие слои фактически становятся текстурой на плоскости, которой удобно (а самое главное — очень дёшево) можно применять различные 3D-трансформации. Но тут есть один очень важный нюанс: если хотя бы один пиксель текстуры поменяется (например, на ховер, анимацию, что-то ещё) — её нужно заново перерисовать. Это очень сильно заметно, например, в WebKit на iOS. Соответственно, неумелое использование GPU-триггеров для «ускорения» страницы может на самом деле привести к очень серьёзным тормозам.

Из всего вышесказанного конкретно для вашей ситуации: скорее всего, источник проблемы не в самом box-shadow, а в неправильной композиции, которая вызывает неявный вынос на GPU ненужных слоёв и, соответственно, перерисовку тени. Потому что при правильной композиции можно обвешаться различными эффектами и ничего не будет тормозить. Наверняка при включении “Show paint rectangles” вы наблюдали, что область перерисовки занимает всю станицу, хотя по-хорошему так быть не должно.

Дебажить такие вещи очень удобно Safari Web Inspector: там есть отдельная панелька Layers, которая для выделенного элемента показывает, какие слои вынесены на GPU и, что особенно важно, причину такого выноса. Лично я уже нескольким ребятам помог сильно оптимизировать производительность с помощью этого инструмента, особенно для мобилок :)

Чуть подробнее об этом можно почитать тут: www.chromium.org/developers/design-documents/gpu-accelerated-compositing-in-chrome
Проблема source maps в том, что они годятся только для просмотра. Как только вы что-нибудь поменяете в сгенерированном CSS или в исходном файле препроцессора — source map становится невалидным и его нужно заново сгенерировать. В итоге для того, чтобы увидеть любое изменение, нужно сначала сохранить файл, дождаться, пока он перегенерируется, потом дождаться, пока Chrome по таймауту подцепит изменения. То есть не совсем «живое» редактирование.

LiveStyle пытается иначе решить эту проблему: свойства в селекторах напрямую мапятся из CSS в препроцессор и наоборот. Тогда получается действительно живое редактирование, фрагмент показан в демо-видео. Маппинг селекторов, в большинстве случаев, относительно простой, но есть проблемы с маппингом миксинов, переменных и прочего. Поэтому я сейчас исследую, как это можно заставить работать правильно и удобно.
Проблема подхода в Chrome Workspaces в том, что он становится бесполезным, если работаете с сайтом, где CSS собирается из нескольких файлов, а потом ещё и минифицируется. С LiveStyle таких проблем нет: вы сможете смапить два абсолютно разных файла (например, исходный модуль CSS в редакторе → скомпилированный CSS в браузере) и использовать их для живого редактирования.

Ну и куча дополнительных плюшек вроде синхронизаций изменений между окнами, быстрое добавление нового файла и т.д.
Он пока не доведён до нужного мне состояния. Это влияет на то, как вы сможете расширять Emmet. В остальном он работает как надо.

Разработчики Netbeans обещали ответить на мои вопросы, но пока молчат. Наверно, скоро «официально» выпущу этот плагин.
1
23 ...

Information

Rating
Does not participate
Location
Санкт-Петербург, Санкт-Петербург и область, Россия
Works in
Date of birth
Registered
Activity