Bỏ qua để vào nội dung chính
CSS Animation Performance: Stick to These Properties to Avoid Jank

CSS Animation Performance: Stick to These Properties to Avoid Jank

Bởi Lucas Fischer
19 thg 4, 20266 phút đọc

CSS animations can be silky smooth or frame-dropping disasters depending on which properties you animate. Learn the compositor-friendly properties and the rendering pipeline behind them.

The Rendering Pipeline

Every frame a browser renders goes through a pipeline: JavaScript, Style, Layout, Paint, Composite. Animations that trigger layout or paint stages are expensive; animations that only trigger the composite stage are cheap and run on the GPU.

The Two Properties to Animate

For virtually all animations, stick to these two CSS properties:

  • transform — for position, scale, rotation
  • opacity — for fading in/out

Both are handled by the compositor thread and run on the GPU. They never trigger layout or paint.

/* GOOD: compositor-only properties */
.card {
  transition: transform 200ms ease, opacity 200ms ease;
  will-change: transform;
}

.card:hover {
  transform: translateY(-4px) scale(1.02);
  opacity: 0.9;
}

/* BAD: triggers layout on every frame */
.card-bad:hover {
  top: -4px;
  width: 110%;
}

Translating vs Positioning

Never animate left/top/right/bottom — always use transform: translate():

/* BAD: causes layout recalculation */
@keyframes slide-bad {
  from { left: -100%; }
  to { left: 0; }
}

/* GOOD: compositor only */
@keyframes slide-good {
  from { transform: translateX(-100%); }
  to { transform: translateX(0); }
}

/* Modal entrance animation */
@keyframes modal-in {
  from {
    transform: scale(0.95) translateY(-10px);
    opacity: 0;
  }
  to {
    transform: scale(1) translateY(0);
    opacity: 1;
  }
}

will-change: Use It Carefully

will-change hints to the browser to promote an element to its own GPU layer. Speeds up animation start but costs GPU memory:

// Good: add/remove dynamically
button.addEventListener('mouseenter', () => {
  target.style.willChange = 'transform';
});
target.addEventListener('transitionend', () => {
  target.style.willChange = 'auto';
});

// BAD: applying will-change globally wastes GPU memory
// * { will-change: transform; } -- Never do this

Properties That Trigger Layout (Avoid in Animations)

  • Dimensions: width, height, padding, margin
  • Positioning: top, left, right, bottom
  • Font and display: font-size, display, border-width

Properties That Trigger Paint Only

  • background-color, color, border-color
  • box-shadow, outline

These skip layout but still repaint. Acceptable for infrequent changes but avoid in 60fps loops.

Debugging With DevTools

Open Chrome DevTools, go to Rendering panel, and enable "Layer borders" and "Paint flashing". Green borders show GPU layers; red flashes show areas being repainted. Use the Performance panel to record and confirm no layout thrash in your animations.

Summary

The rule is simple: animate only transform and opacity. Use will-change sparingly. Check DevTools to verify your animations stay on the compositor thread. Following these rules is the difference between smooth 60fps and janky 15fps.

Không spam, hủy đăng ký bất kỳ lúc nào.

Bài viết liên quan