will-change — подсказка браузеру
will-change— CSS-свойство, сообщающее браузеру, какие свойства элемента изменятся в ближайшем будущем, позволяя браузеру заранее оптимизировать: создать compositor layer для GPU-ускорения анимаций трансформаций и прозрачности.
Зачем нужно
Без will-change браузер анимирует transform/opacity на CPU в main thread, что вызывает jank (пропуски кадров) при сложных страницах. С will-change: transform браузер переносит элемент на GPU compositor thread — анимация идёт независимо от main thread.
Где используется
- CSS-анимации с
transformиopacity(slidein, fadeout, модальные окна) - Scroll-triggered анимации
- Параллакс эффекты
- Drag-and-drop элементы
Основной контент
Базовое использование
/* Анимированный модальный диалог */
.modal {
will-change: transform, opacity;
/* Браузер создаёт compositor layer ЗАРАНЕЕ */
/* Анимация идёт плавно на 60fps */
}
.modal.entering {
animation: slideIn 0.3s ease-out;
}
.modal.leaving {
animation: slideOut 0.2s ease-in;
}
@keyframes slideIn {
from { transform: translateY(-20px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
Правильный паттерн: динамическое добавление
// Добавляем will-change ПЕРЕД анимацией, убираем ПОСЛЕ
const button = document.querySelector('.animate-btn');
const card = document.querySelector('.card');
button.addEventListener('click', () => {
// Шаг 1: Подготовка layer перед анимацией
card.style.willChange = 'transform';
// Шаг 2: Запуск анимации
card.classList.add('animating');
// Шаг 3: Удаление после завершения
card.addEventListener('animationend', () => {
card.style.willChange = 'auto';
card.classList.remove('animating');
}, { once: true });
});
Scroll-triggered: only when near viewport
const observer = new IntersectionObserver(
(entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// Готовимся к анимации при появлении
entry.target.style.willChange = 'transform, opacity';
} else {
// Убираем layer для невидимых элементов
entry.target.style.willChange = 'auto';
}
});
},
{ rootMargin: '200px' } // Готовим за 200px до viewport
);
document.querySelectorAll('.animated').forEach(el => observer.observe(el));
Что НЕ работает с will-change
/* will-change только для свойств без layout */
/* ХОРОШО: compositor-only properties */
.el { will-change: transform; } /* GPU layer ✓ */
.el { will-change: opacity; } /* GPU layer ✓ */
.el { will-change: filter; } /* GPU layer ✓ */
/* ПЛОХО: вызывает layout — will-change не помогает */
.el { will-change: width; } /* Требует relayout */
.el { will-change: margin; } /* Требует relayout */
.el { will-change: top; } /* Используй transform: translateY */
Память: стоимость compositor layer
/* Каждый will-change element = compositor layer = память GPU */
/* Для изображения 1920x1080: ~8MB GPU памяти */
/* ПЛОХО: will-change на всех элементах */
* { will-change: transform; } /* Съест всю VRAM */
/* ХОРОШО: только на том, что реально анимируется */
.header.sticky { will-change: transform; }
Частые ошибки
will-changeна всех элементах — истощает память GPU, может замедлитьwill-change: autoна анимируемых элементах — не даёт никакого эффекта- Использование вместо
transformдля позиционирования — переносите на transform/opacity - Не удалять
will-changeпосле анимации — layer остаётся в памяти навсегда
Связанные темы
- _MOC Производительность
- contain -- CSS Containment
- Как браузер рендерит страницу
- Core Web Vitals -- LCP, FID, CLS