Установи и проверь performance-бюджет для motion в проекте. Целевое устройство: {{target_device}}. Цель — анимации, которые не отнимают у пользователя scroll, ввод и батарею.
1. Бюджет (hard limits)
| Метрика | Бюджет | Где замерять |
|---|---|---|
| FPS во время анимации | ≥ 60 FPS на mid-range mobile | DevTools Performance → FPS meter |
| INP (взаимодействие во время motion) | < 200ms | DevTools Performance Insights, real user monitoring |
| Длительность главной анимации | ≤ 400ms (UI) / ≤ 1000ms (orchestration) | таймлайн |
| GPU memory от will-change | < 30 MB суммарно | DevTools → Performance → GPU |
| Long tasks от motion JS | 0 задач > 50ms | DevTools → Performance |
| CLS от анимации | 0.0 | Lighthouse, web-vitals lib |
Если хоть один пункт красный — анимация не релизится.
2. Только composite-friendly свойства
GOOD (compositor only, GPU)
transform: translate / scale / rotate
opacity
filter (с осторожностью — на старых GPU дорогой)
BAD (layout / paint, CPU)
width / height / top / left / right / bottom
margin / padding
border-width
font-size
background-color (paint)
box-shadow (paint, особенно крупный blur)
/* плохо */
.menu { transition: left 300ms; left: 0; }
.menu.closed { left: -300px; }
/* хорошо */
.menu { transition: transform 300ms; transform: translateX(0); }
.menu.closed { transform: translateX(-100%); }
3. will-change — правильно
/* НЕПРАВИЛЬНО — всегда включено */
.card { will-change: transform; } /* съедает GPU memory вечно */
/* ПРАВИЛЬНО — только во время анимации */
.card { transition: transform 250ms; }
.card:hover { will-change: transform; transform: translateY(-4px); }
.card { /* JS убирает will-change по transitionend */ }
function animate(el: HTMLElement) {
el.style.willChange = "transform, opacity";
el.classList.add("active");
el.addEventListener("transitionend", () => {
el.style.willChange = "auto"; // отпускаем GPU
}, { once: true });
}
Правило: will-change — обещание браузеру. Когда оно вечное, браузер держит layer навсегда → GPU memory leak → тротлинг на mobile.
4. GPU layers — где они нужны
Принудительно создавай compositor layer только для элементов, которые реально анимируются:
.animated-card {
transform: translateZ(0); /* или translate3d(0,0,0) — promotes to GPU layer */
}
- Слишком много layers (100+) → GPU memory pressure → throttle.
- Слишком мало → каждая анимация триггерит paint/composite → jank.
- Sweet spot: layer на каждом независимо анимирующемся элементе, не больше.
DevTools → More tools → Layers — посмотри, сколько у тебя их сейчас.
5. Optimization checklist
- Все анимации используют только
transformиopacity(нет height/top/box-shadow) - Нет вечного
will-change— только pre-animation, снимается after -
requestAnimationFrameдля всех scroll/resize handlers -
{ passive: true }на scroll/touch listeners - Нет
getBoundingClientRect/offsetWidthвнутри rAF (force reflow) - Изображения в анимации имеют фиксированные width/height (нет CLS)
- Анимация выключена за пределами viewport (CSS
animation-play-stateили IO) -
prefers-reduced-motionобработан везде - Lottie/Rive — препроцессированы, ≤ 50 KB на анимацию
- Heavy SVG-фильтры (
feGaussianBlur,feTurbulence) — НЕ на mobile - Backdrop-filter — НЕ более чем на 1 элементе на экране
- Видео в hero —
poster,preload="metadata", autoplay только при visibility
6. Как мерить
FPS
- DevTools → Rendering →
Frame Rendering Stats - CPU throttling: 4x slowdown
- Network: Fast 3G
- Сними 5-секундный record во время анимации
- Зелёный график (стабильно 60) или красный (drops)?
INP
- DevTools → Performance Insights → live mode
- Кликни во время анимации
- Найди event в треке →
Interaction - Длительность ≤ 200ms?
Real device
- WebPageTest на реальном Moto G Power / iPhone SE.
- Chrome DevTools Remote Debugging для Android.
- Safari Web Inspector для iOS.
7. Когда снимать с релиза
- FPS < 45 на target device → откладывай или упрощай (короче, меньше элементов).
- INP > 300ms → анимация блокирует interaction. Уменьши scope или убери JS-handler.
- Long task > 100ms от motion library — рефактор (lazy load Framer Motion, debounce, virtualize).
- GPU memory > 50 MB на странице — мобильный Safari прибьёт вкладку.
8. Формат вывода
## Motion performance audit
### Замеры (до)
| Метрика | Значение | Бюджет | Статус |
|---|---|---|---|
| FPS hero anim | 42 | 60 | FAIL |
| INP during scroll | 280ms | 200 | FAIL |
| Layer count | 187 | <50 | FAIL |
### Найденные проблемы
1. `.hero-bg { animation: gradient-shift }` — paint каждый кадр
2. `.card { will-change: transform }` 96 штук вечно — 28MB GPU
3. ...
### Фиксы
[патчи]
### Замеры (после)
[таблица]
Анти-паттерны
- ❌
will-change: transformна 50+ карточках вечно — 30+ MB GPU съедено, fps дропает даже без анимации. - ❌ Анимация
heightот 0 до auto — layout reflow каждый кадр, всегда jank. - ❌
box-shadowtransition с большим blur — paint-bound, 30 FPS на mobile. - ❌
backdrop-filter: blurна скроллирующейся карточке — full-screen repaint каждый кадр. - ❌ Замер FPS без CPU throttling — на M2 Pro всё летает, на Moto G тормозит.
- ❌ Animating SVG filter — самая дорогая операция, на mobile почти всегда < 30 FPS.
- ❌ Lottie 500 KB+ JSON — парсинг блокирует main thread на 500-1000ms.
- ❌ Hero видео без
preload="metadata"и безposter— гонка с LCP, CLS up. - ❌ Не выключать infinite-анимации вне viewport — батарея и CPU горят за зря.
- ❌ Релизить motion без замера на реальном устройстве — DevTools throttling не = реальный mobile GPU.
Аудит производительности (Core Web Vitals)
Глубокая проверка LCP, INP, CLS с привязкой к коду и приоритизированным планом исправлений.
Мастер-аудит сайта: 6 измерений за один проход
Orchestrator-аудит по 6 направлениям: UX, accessibility, performance, SEO, brand consistency, security. Quick scan + deep dive + приоритизированный план + композитная оценка + roadmap.
Performance budget по типам страниц
Бюджеты JS/CSS/images для разных типов страниц, целевые Web Vitals, enforcement в CI с конкретными порогами.