Картинки ужали вдвое, а Safari на iPhone всё равно перезагружал вкладку — теперь сразу, без прокрутки, по нажатию на меню. Выключили размытие фона у меню — падать перестало, но выезд стал рваным. Размытие оказалось спусковым крючком, а причиной было то, что под ним: 769 слоёв композитинга.

Это четвёртая часть цикла «Вёрстка без сюрпризов». Первая половина истории — про картинки — в третьей части.

Что такое слой и почему их много

Браузер выносит часть элементов в отдельные слои, которые рисуются видеокартой: так их можно двигать и анимировать без перерисовки страницы. У каждого слоя свой буфер в памяти — ширина × высота × 4 байта × квадрат плотности экрана. Десяток слоёв — это норма. Сотни — это сотни буферов.

Полноэкранное размытие (backdrop-filter) просит браузер размыть всё, что под элементом. Над лёгкой страницей это дёшево, над сотнями слоёв — память кончается.

Как посчитать слои

Счёт слоёв даёт протокол инструментов разработчика Chrome: LayerTree.enable присылает дерево слоёв, а LayerTree.compositingReasons объясняет, почему элемент стал слоем. Это Chrome, а не WebKit, но он показывает главное — кто становится слоем. На верху главной, со всеми загруженными строками, слоёв было 769.

Обычные зонды в Chrome падение Safari не ловят: все наши замеры после первой правки были «зелёные», а у пользователя вкладка падала. Первое, что делать при жалобе «Safari вылетает», — считать слои.

Кто давал слои

  1. Swiper в Safari сам включает режим .swiper-backface-hidden, а с ним каждому слайду — transform: translateZ(0) и backface-visibility: hidden. Каждая миниатюра полосы под слайдером стала слоем — около 200 на главной;
  2. translate3d(…) для центрирования миниатюр, кадров галереи, фона категории — трёхмерный сдвиг всегда создаёт слой;
  3. clip-path на классе, который стоял и на контейнере, и на каждом слайде, — около 190 масок-слоёв;
  4. выезд меню через right: -100% → 0 — пересчёт раскладки на каждом кадре анимации.

Лечение

/* 1. полосу двигает сдвиг обёртки — самим миниатюрам слой не нужен */
.thumbsStrip.swiper-backface-hidden .swiper-slide {
  transform: none;
  backface-visibility: visible;
}

/* 2. центрирование двумерным сдвигом — визуально то же самое */
.thumb img { transform: translate(0, -50%); }   /* было translate3d(0, -50%, 0) */

/* 3. маска только контейнеру и только там, где она нужна */
@media (min-width: 821px) {
  .productSlider { clip-path: inset(0); }
}

/* 4. выезд меню сдвигом, а не свойством раскладки */
.mobileMenu       { transform: translateX(100%); }
.mobileMenu.open  { transform: translateX(0); }

И пятое — ограничитель по построению: на устройствах без мыши строки дальше полутора экранов от видимой области получают класс, который прячет содержимое через visibility: hidden. Раскладка и загрузка картинок не меняются, а буферы освобождаются. Память перестала расти с числом прокрученных строк — около четырёх строк в любой момент.

const io = new IntersectionObserver((entries) => {
  entries.forEach((e) => e.target.classList.toggle('isFar', !e.isIntersecting));
}, { rootMargin: '150%' });
if (matchMedia('(hover: none)').matches) {
  document.querySelectorAll('.productLine').forEach((line) => io.observe(line));
}

Результат

  • 769 → 22 слоя;
  • у пользователя на айфоне вкладка не падает, меню выезжает плавно, размытие вернули;
  • мерцания при листании слайдов без трёхмерных трюков не замечено.

Что грепать в своём коде: translate3d, translateZ, backface-visibility, clip-path, backdrop-filter, will-change, filter:. Каждое — кандидат в слой. Проверять на настоящем устройстве после каждого шага, на тестовой копии до выката.

Safari на айфоне перезагружает вкладку на вашем сайте?

Пришлите адрес — посчитаю слои и найду, кто их плодит.

· полезно?
АЧ
Алексей Чикун веб-разработчик, команда TRIOHEAD

Делаю сайты и сервисы целиком: от вёрстки до сервера. Пишу о том, что проверил на своих проектах.

Комментарии

// загружаю комментарии…

Комментарии публикуются после проверки. Ссылки и HTML не принимаются.