Канвас, который выдавил страницу за экран: как замер раздувал вёрстку
Первый экран уезжал вправо на телефоне. Виновата была одна строка, записывавшая измеренную ширину обратно в стиль — и цикл, который наращивал её при каждом замере.
На телефоне первый экран уезжал за правый край: заголовок обрезан, кнопка за пределами видимой области, горизонтальная прокрутка. На десктопе всё было в порядке. Причина оказалась в четырёх строках, которые выглядели совершенно безобидно.
Что было видно
Замер показал странное: контейнер первого экрана 327 пикселей, а сам блок с роликом — 345 и смещён вправо. Вложенный элемент шире родителя, при том что у него задана ширина 100%. Такого не бывает, если ширина 100% берётся от родителя.
Значит, ширину задавало что-то другое.
Причина: канвас сам себе размер
Код подготовки канваса измерял контейнер и записывал результат в два места: в атрибуты width и height — это буфер пикселей, и в инлайновый стиль — это размер на странице.
Второе и есть ошибка. Канвас с инлайновой шириной в пикселях становится блоком фиксированного размера, а значит его min-content равен этой ширине. Родительская колонка сетки была автоматической, то есть подстраивалась под содержимое, — и она обязана была стать не меньше канваса. Дальше цикл: колонка шире, следующий замер даёт большую ширину, стиль обновляется, колонка снова шире.
1const resize = () => {
2 const rect = box.getBoundingClientRect();
3 const ratio = Math.min(devicePixelRatio || 1, 2);
4 width = rect.width;
5 height = rect.height;
6
7 node.width = Math.round(width * ratio);
8 node.height = Math.round(height * ratio);
9
10 // Было — и это самораскручивающийся цикл:
11 // node.style.width = `${width}px`;
12 // node.style.height = `${height}px`;
13
14 context.setTransform(ratio, 0, 0, ratio, 0, 0);
15};
Починка в двух местах
Первое: убрать запись стиля. Размер канваса на странице задаёт CSS — сто процентов от контейнера.
Второе, важнее: сделать колонку сетки такой, чтобы она вообще не могла раздуться содержимым. minmax(0, 1fr) вместо автоматической. Тогда любой слишком широкий ребёнок будет прокручиваться внутри себя, а не растягивать страницу.
1.hero-film {
2 display: grid;
3 /* Строки: шаги, слова, картинка, абзац. */
4 grid-template-rows: auto auto minmax(0, 1fr) auto;
5 /* Одна колонка — та, что есть, а не та, которую хочет содержимое. */
6 grid-template-columns: minmax(0, 1fr);
7}
8
9.hero-film-stage canvas {
10 width: 100%;
11 max-width: 100%;
12 height: 100%;
13 display: block;
14}
CLS измеряет визуальную стабильность. Хорошим считается значение до 0,1 — по реальным посетителям, на 75-м процентиле.
Как проверять такое у себя
- ✓Сравнить scrollWidth документа с innerWidth — есть ли горизонтальная прокруткауже так
- ✓Найти элемент, который шире родителя, и посмотреть, откуда у него ширинауже так
- ✓Проверить, нет ли инлайновых стилей, записанных из JavaScriptуже так
- ✓Заменить автоматические колонки сетки на minmax(0, 1fr)уже так
- ✓Повторить замер на 375 пикселях, а не только на десктопеуже так
