Разберись в четырёх фазах отрисовки в браузере, чтобы точно знать, какие свойства CSS обходятся дорого и как will-change работает изнутри.
Открыть этот урок в KodokonБраузер превращает твой CSS в пиксели через конвейер из четырёх фаз: Style (вычисление значений), Layout (геометрия, он же reflow), Paint (растеризация в пиксели, тот самый repaint) и Composite (сборка слоёв на GPU). Золотое правило: чем более ранней фазы ты касаешься, тем больше счёт, потому что каждая следующая фаза проигрывается заново. Изменение width или top запускает layout + paint + composite. Изменение color или box-shadow пропускает layout, но перерисовывает. Только transform и opacity может обработать один поток композитора: анимация остаётся плавной, даже когда основной поток заблокирован JavaScript.
.slide-bad {
position: absolute;
transition: left 0.3s ease;
}
.slide-bad:hover { left: 120px; }
.slide-good {
transition: transform 0.3s ease;
}
.slide-good:hover { transform: translateX(120px); }Браузер ленив: он копит твои записи стилей и пересчитывает layout только к следующему обновлению кадра. Но некоторые чтения из JavaScript - offsetHeight, getBoundingClientRect(), scrollTop, getComputedStyle() по геометрическому свойству - требуют актуальных значений. Если запись ещё не применена, браузер обязан пересчитать всё немедленно и синхронно: это и есть принудительный reflow. Чередование чтений и записей внутри цикла порождает layout thrashing: один полный reflow на каждую итерацию вместо одного на весь пакет.
const items = document.querySelectorAll('.item');
items.forEach((item) => {
const h = item.offsetHeight;
item.style.height = h * 2 + 'px';
});
const heights = [...items].map((i) => i.offsetHeight);
items.forEach((item, index) => {
item.style.height = heights[index] * 2 + 'px';
});Композитинг нарезает страницу на слои, которые растеризуются независимо и собираются на GPU. will-change: transform просит поднять элемент в собственный слой до анимации, избавляя от дорогой растеризации на первом кадре. Тонкость спецификации, о которой часто забывают: will-change обязан давать те же побочные эффекты, что и свойство, которое он анонсирует. Поэтому will-change: transform создаёт контекст наложения и превращает элемент в содержащий блок для его потомков с position: fixed - ровно как настоящий transform. Простая подсказка о производительности может, таким образом, сломать твой порядок наложения или твои фиксированные элементы.
const panel = document.querySelector('.panel');
panel.addEventListener('pointerenter', () => {
panel.style.willChange = 'transform';
});
panel.addEventListener('transitionend', () => {
panel.style.willChange = 'auto';
});