my_nuxt_blog/content/en/articles/css-animation-performance.md
2026-06-08 19:55:40 +08:00

2.0 KiB
Raw Permalink Blame History

title description date tags image
CSS Animation Performance: From Jank to 60fps Practical tips for smoother CSS animations by understanding the rendering pipeline and avoiding expensive layout/paint work. 2026-05-20
CSS
performance
frontend

Why animations drop frames

The browser rendering pipeline is roughly: JavaScript → Style → Layout → Paint → Composite.

Most “janky” animations are caused by triggering Layout or Paint too often. At 60fps you only get about 16ms per frame — if layout/paint takes too long, frames get dropped.

Prefer properties that only hit Composite

transform and opacity can be handled on the compositor thread, avoiding layout and paint:

/* ✅ Recommended: composite-only */
.element {
  transform: translateX(100px);
  opacity: 0.5;
}

/* ❌ Avoid: triggers layout */
.element {
  left: 100px;
  width: 200px;
}

Use will-change (carefully)

.element {
  will-change: transform;
}

This hints the browser that the element is likely to change soon, which can help it prepare a separate layer. Dont overuse it — extra layers consume GPU memory.

Use the FLIP technique

For animations that cant be expressed purely as transforms, FLIP is a reliable pattern:

  1. First: measure the initial position
  2. Last: measure the final position
  3. Invert: apply the delta as an inverse transform
  4. Play: remove the inverse transform with a transition
const first = el.getBoundingClientRect()
// mutate layout...
const last = el.getBoundingClientRect()
const invert = first.top - last.top

el.style.transform = `translateY(${invert}px)`
requestAnimationFrame(() => {
  el.style.transition = 'transform 0.3s'
  el.style.transform = ''
})

Summary

One guiding principle: aim for composite-only animations whenever possible. Prefer transform over top/left, and use opacity when you can. Add will-change and FLIP when they truly help.