ToolHub
View All Posts

Hướng Dẫn Hoạt Ảnh CSS: Từ Transition Đến Keyframe

Hoạt ảnh CSS làm cho giao diện web trở nên sống động. Khi thực hiện đúng, chúng hướng dẫn sự chú ý, cung cấp phản hồi và làm cho tương tác cảm thấy tự nhiên. Khi thực hiện kém, chúng gây khó chịu, làm chậm trang và loại trừ người dùng có vấn đề về tiền đình. Hướng dẫn này bao gồm mọi thứ bạn cần để tạo hoạt ảnh hiệu suất cao, dễ tiếp cận, hoạt động trên tất cả các trình duyệt hiện đại.

Transition CSS: Nền Tảng

Transition CSS là cách đơn giản nhất để thêm hoạt ảnh vào trang web của bạn. Chúng nội suy giữa hai trạng thái khi giá trị thuộc tính CSS thay đổi — thường được kích hoạt bởi :hover, :focus hoặc thay đổi class.

Cú Pháp Transition

Cú pháp transition là một shorthand cho bốn thuộc tính. Hiểu từng thuộc tính là điều cần thiết để kiểm soát chính xác cách hoạt ảnh của bạn hoạt động.

.button {
  background: #3b82f6;
  transform: scale(1);
  transition: background 0.3s ease, transform 0.2s ease;
}

.button:hover {
  background: #2563eb;
  transform: scale(1.05);
}

Bạn có thể áp dụng transition cho nhiều thuộc tính cùng lúc bằng cách phân tách chúng bằng dấu phẩy, hoặc sử dụng transition-property: all để transition mọi thuộc tính có thể hoạt ảnh. Tuy nhiên, sử dụng all có thể gây ra vấn đề hiệu suất vì trình duyệt phải kiểm tra mọi thay đổi thuộc tính.

Nhiều Transition

Hàm easing kiểm soát tốc độ thay đổi trong suốt quá trình transition. Chúng định nghĩa đường cong gia tốc — cách nhanh hoặc chậm hoạt ảnh di chuyển tại các điểm khác nhau. Easing phù hợp tạo ra sự khác biệt giữa hoạt ảnh cảm thấy tự nhiên và hoạt ảnh cảm thấy máy móc.

@keyframes pulse {
  0% {
    transform: scale(1);
    opacity: 1;
  }
  50% {
    transform: scale(1.05);
    opacity: 0.8;
  }
  100% {
    transform: scale(1);
    opacity: 1;
  }
}

.pulse-element {
  animation: pulse 2s ease-in-out infinite;
}

Các Hàm Easing

HàmĐường CongMô Tả
easeTăng tốc nhanh, chậm dầnMặc định, tốt cho hầu hết UI
Nút, thẻ, hoverlinearTốc độ không đổi
Máy móc, hiếm khi tự nhiênSpinner, thanh tiến trìnhease-in
Bắt đầu chậm, kết thúc nhanhDi chuyển ra khỏi màn hìnhPhần tử rời đi
ease-outBắt đầu nhanh, kết thúc chậmDi chuyển vào màn hình
Phần tử xuất hiệnease-in-outBắt đầu chậm, nhanh, kết thúc chậm
Chuyển động tự nhiên nhấtHầu hết hoạt ảnhcubic-bezier()

Keyframe Animation: Sức Mạnh Toàn Diện

Trong khi transition xử lý các thay đổi hai trạng thái đơn giản, @keyframes cho phép bạn định nghĩa hoạt ảnh nhiều bước phức tạp với toàn quyền kiểm soát các khung hình trung gian. Bạn có thể chỉ định chính xác những gì xảy ra tại mỗi điểm trong quá trình hoạt ảnh.

Cú Pháp @keyframes

Animation shorthand kết hợp tám thuộc tính thành một khai báo. Như transition, hiểu thứ tự và mục đích của từng thành phần giúp bạn viết hoạt ảnh rõ ràng hơn, dễ bảo trì hơn.

@keyframes bounce {
  0%, 100% {
    transform: translateY(0);
  }
  25% {
    transform: translateY(-20px);
  }
  50% {
    transform: translateY(0);
  }
  75% {
    transform: translateY(-10px);
  }
}

Bạn có thể kiểm soát số lần hoạt ảnh lặp lại (bao gồm phân số và vô hạn) và liệu nó có đảo ngược hướng khi lặp lại hay không. Animation-direction: alternate đặc biệt hữu ích để tạo hoạt ảnh qua lại mà không cần định nghĩa keyframe riêng biệt.

Thuộc Tính Animation

Thuộc tính animation-play-state cho phép bạn tạm dừng và tiếp tục hoạt ảnh. Điều này rất có giá trị để tôn trọng tùy chọn giảm chuyển động của người dùng, tạm dừng hoạt ảnh khi các phần tử không hiển thị và tạo điều khiển hoạt ảnh tương tác.

.element {
  animation: slideIn 0.5s ease-out 0.2s 1 forwards running;
}

/* Equivalent longhand */
.element {
  animation-name: slideIn;
  animation-duration: 0.5s;
  animation-timing-function: ease-out;
  animation-delay: 0.2s;
  animation-iteration-count: 1;
  animation-direction: normal;
  animation-fill-mode: forwards;
  animation-play-state: running;
}

Lặp Lại và Hướng

Không phải tất cả thuộc tính CSS đều có thể được hoạt ảnh. Các thuộc tính chấp nhận giá trị số thường có thể được hoạt ảnh, trong khi các thuộc tính như display hoặc position (với từ khóa) thì không. Hiểu thuộc tính nào có thể được hoạt ảnh và thuộc tính nào rẻ về mặt hiệu suất là rất quan trọng để tạo hoạt ảnh mượt mà.

Thuộc Tính Animation Shorthand

Hiệu suất kém là lý do phổ biến nhất khiến hoạt ảnh trông tệ. Hiểu cách trình duyệt hiển thị và những thuộc tính nào kích hoạt các giai đoạn pipeline khác nhau giúp bạn viết hoạt ảnh chạy ở tốc độ 60fps mượt mà.

Trạng Thái Play

Các Thuộc Tính Tối Ưu Cho Hoạt Ảnh

Để đạt được hoạt ảnh 60fps, hãy giới hạn hoạt ảnh chỉ ở thuộc tính transform và opacity. Hai thuộc tính này có thể được xử lý hoàn toàn bởi GPU trong giai đoạn compositor, bỏ qua các giai đoạn layout và paint tốn kém. Transform xử lý vị trí, tỷ lệ và xoay; opacity kiểm soát độ trong suốt.

.element {
  /* Smooth deceleration - great for entrances */
  transition: transform 0.3s cubic-bezier(0.0, 0.0, 0.2, 1.0);

  /* Smooth acceleration - great for exits */
  transition: transform 0.2s cubic-bezier(0.4, 0.0, 1, 1);

  /* Material Design standard easing */
  transition: transform 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
}

GPU Acceleration

will-change gợi ý cho trình duyệt rằng một phần tử sẽ sớm được hoạt ảnh, cho phép nó chuẩn bị trước tối ưu hóa. Sử dụng ít, chỉ trên các phần tử sẽ thực sự hoạt ảnh và gỡ bỏ sau khi hoạt ảnh kết thúc.

@keyframes typing {
  from { width: 0; }
  to { width: 100%; }
}

.typing-effect {
  animation: typing 3s steps(40, end);
  overflow: hidden;
  white-space: nowrap;
}

Các Thuộc Tính Có Thể Hoạt Ảnh

Đối với hoạt ảnh dựa trên JavaScript, luôn sử dụng requestAnimationFrame thay vì setInterval hoặc setTimeout. Nó đồng bộ với chu kỳ làm mới của trình duyệt, ngăn chặn hoạt ảnh khi tab không hiển thị và nhóm các cập nhật để có hiệu suất mượt mà hơn.

will-change

Hoạt ảnh nên tăng cường trải nghiệm người dùng, không gây hại cho nó. Người dùng có rối loạn tiền đình có thể gặp chóng mặt, buồn nôn hoặc mất phương hướng từ hoạt ảnh chuyển động. Tôn trọng tùy chọn giảm chuyển động của họ không phải là tùy chọn.

  1. animation-direction: Hướng phát (normal, reverse, alternate, alternate-reverse).
  2. animation-fill-mode: Cách hoạt ảnh áp dụng style trước/sau khi phát.
  3. animation-play-state: Kiểm soát phát/tạm dừng (running, paused).
  4. Composite: Combine the painted layers in the correct order. Only this step runs on the GPU. Triggered by changes to transform and opacity.

Media query prefers-reduced-motion cho phép bạn phát hiện khi người dùng đã yêu cầu giảm chuyển động ở cấp hệ điều hành. Thiết kế hoạt ảnh của bạn như sự tăng cường tiến bộ: bắt đầu với trải nghiệm tĩnh hoạt động, sau đó thêm hoạt ảnh cho người dùng không có tùy chọn giảm chuyển động.

Sử Dụng requestAnimationFrame

Sử DụngThuộc TínhHoạt Ảnh Được?Ghi Chú
Đường cong tùy chỉnh 4 điểmKiểm soát hoàn toànEasing tùy chỉnhsteps()
Bước rời rạcHiệu ứng stop-motionHoạt ảnh spritetransform
Chỉ compositor, hiệu suất tốt nhấtopacity
Chỉ compositorcolorKích hoạt paint
background-colorKích hoạt paintwidth
Kích hoạt layout, tránhheight
Kích hoạt layout, tránhleft/topKích hoạt layout, dùng transform thay thế
displayKhôngKhông thể transition, dùng opacity + visibilitybackground-image

Fade In

Hoạt ảnh cuộn trang (scroll-driven) liên kết tiến trình hoạt ảnh với vị trí cuộn, tạo ra trải nghiệm kể chuyện sống động khi người dùng cuộn qua nội dung.

/* Apply will-change just before the animation */
.element:hover {
  will-change: transform;
}

/* Remove it after the animation completes */
.element {
  transition: transform 0.3s ease;
}

Nắm vững hoạt ảnh CSS cần thực hành. Bắt đầu với transition đơn giản, xây dựng dần lên keyframe animation và luôn kiểm tra trên thiết bị thực tế. Sử dụng các công cụ dưới đây để tăng tốc quy trình làm việc của bạn.

Slide In

Transition áp dụng cho thay đổi trạng thái đơn giản (A đến B), trong khi keyframe animation cho phép nhiều bước và hoạt ảnh phức tạp. Transition yêu cầu trigger (hover, focus, class change), animation có thể tự động phát khi tải trang. Transition chỉ chạy một lần, animation có thể lặp vô hạn. Sử dụng transition cho tương tác UI, animation cho hiệu ứng trang trí và tải.

Mẹo chuyên nghiệp: Luôn kiểm tra hoạt ảnh trên thiết bị di động thực tế. CPU di động yếu hơn nhiều so với máy tính để bàn và hoạt ảnh chạy mượt mà trong DevTools có thể bị giật trên điện thoại tầm trung. Sử dụng Performance tab trong DevTools để đo FPS và xác định các nút thắt cổ chai.

Hiệu Suất Hoạt Ảnh

Luôn ưu tiên transform và opacity vì chúng chỉ ảnh hưởng đến giai đoạn compositor, bỏ qua layout và paint. Tránh hoạt ảnh width, height, top, left và các thuộc tính kích hoạt layout. Sử dụng translateX/Y thay vì left/top, scale thay vì width/height. Sử dụng will-change một cách tiết kiệm và gỡ bỏ nó sau khi hoạt ảnh kết thúc.

Pulse

Sử dụng prefers-reduced-motion: reduce để tắt hoặc đơn giản hóa hoạt ảnh. Cung cấp toggle để bật/tắt hoạt ảnh. Tránh hoạt ảnh nhấp nháy hoặc chuyển động lớn có thể kích hoạt vấn đề tiền đình. Giữ thời lượng hoạt ảnh dưới 5 giây khi có thể.

@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

.fade-in {
  animation: fadeIn 0.4s ease-out forwards;
}

Shake

A more dynamic entrance that combines vertical movement with opacity. This creates a sense of the element arriving from below:

@keyframes slideUpFade {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.slide-up {
  animation: slideUpFade 0.5s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}

Spin

A subtle scale increase on hover provides satisfying interactive feedback:

.card {
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.card:hover {
  transform: translateY(-4px) scale(1.02);
  box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);
}

Bounce

When a list of items appears, staggering the animation delay for each item creates a cascading effect that guides the eye:

.list-item {
  opacity: 0;
  transform: translateY(10px);
  animation: slideUpFade 0.3s ease-out forwards;
}

.list-item:nth-child(1) { animation-delay: 0.0s; }
.list-item:nth-child(2) { animation-delay: 0.05s; }
.list-item:nth-child(3) { animation-delay: 0.1s; }
.list-item:nth-child(4) { animation-delay: 0.15s; }
.list-item:nth-child(5) { animation-delay: 0.2s; }

animation-delaystyle="animation-delay: calc(var(--i) * 0.05s)".

prefers-reduced-motion

A smooth, continuous rotation is the classic loading indicator:

@keyframes spin {
  to { transform: rotate(360deg); }
}

.spinner {
  width: 24px;
  height: 24px;
  border: 3px solid rgba(255, 255, 255, 0.2);
  border-top-color: #3b82f6;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

Các Mẫu Hoạt Ảnh Phổ Biến

prefers-reduced-motion media query allows you to respect the user's system-level preference for reduced motion.

Hoạt Ảnh Như Sự Tăng Cường

The recommended approach is to make animations opt-in for users who have not set a reduced motion preference:

/* Default: no animation for users who prefer reduced motion */
.element {
  opacity: 1;
  transform: none;
}

/* Only animate for users who have no motion preference */
@media (prefers-reduced-motion: no-preference) {
  .element {
    animation: slideUpFade 0.5s ease-out forwards;
  }
}

Alternatively, you can disable specific animations for users who prefer reduced motion:

.element {
  animation: slideUpFade 0.5s ease-out forwards;
}

@media (prefers-reduced-motion: reduce) {
  .element {
    animation: none;
    opacity: 1;
    transform: none;
  }

  /* Keep functional transitions but shorten duration */
  * {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

@scroll-timeline

Not all motion should be removed when the user prefers reduced motion. Functional animations that convey meaning or state should be preserved, just simplified:

Hoạt Ảnh Cho Khả Năng Tiếp Cận

While CSS animations handle most UI needs, JavaScript animation libraries provide advanced capabilities like physics-based motion, scroll-linked animations, and orchestration of complex sequences.

animation-timeline

Hoạt Ảnh Tiến Trình Đọc

Hiệu Ứng Parallax

Use CSS for simple, declarative animations that respond to state changes. Use JavaScript when you need: physics-based motion (springs, momentum), scroll-linked animations, complex sequencing with precise timing control, dynamic animation parameters calculated at runtime, or animations that need to be paused, reversed, or scrubbed programmatically.

Hoạt Ảnh Cuộn Trang (Scroll-Driven)

Animation bugs can be subtle and difficult to diagnose. The right debugging tools and techniques make the process much more efficient.

Chrome DevTools Animations Panel

Chrome DevTools includes a dedicated Animations panel that provides powerful debugging capabilities:

Performance Profiling

Use the Performance panel in Chrome DevTools to identify animation jank:

Common Debugging Scenarios

Working with CSS? Try our free CSS tools to format, minify, and optimize your stylesheets for production.

Trình Tạo Hoạt Ảnh CSSCSS Minifier

Công Cụ và Tài Nguyên

Should I use CSS transitions or CSS animations?

Use CSS transitions for simple state changes that go from point A to point B, such as hover effects, focus states, and toggling visibility. Use CSS animations (keyframes) for complex, multi-step sequences, looping animations, or animations that need to run independently of state changes. Transitions are simpler to write; animations offer more control.

What CSS properties are safe to animate for performance?

The most performant properties to animate are transform and opacity, because they can be handled entirely by the GPU compositor without triggering layout or paint. Other properties like width, height, margin, and top trigger layout recalculation, which is expensive. Always prefer transform: translateX() over left, and transform: scale() over width/height changes.

How do I make animations accessible for users with motion sensitivity?

Use the prefers-reduced-motion media query to disable or simplify animations for users who have enabled the 'reduce motion' setting in their operating system. Wrap non-essential animations in @media (prefers-reduced-motion: no-preference) and provide instant state changes as the fallback for users who prefer reduced motion.

What is will-change and when should I use it?

will-change is a CSS property that hints to the browser about which properties will change in the future, allowing it to optimize ahead of time. Use it sparingly and only when you observe performance issues. Apply it just before the animation starts and remove it after the animation ends. Overusing will-change wastes GPU memory and can actually hurt performance.

How do I debug CSS animations?

Chrome DevTools provides powerful animation debugging tools. Open the Animations panel (in the More Tools menu) to see all active animations, scrub through timelines, modify durations and delays in real time, and slow animations down to 1/4 speed. You can also use the Performance panel to identify animation jank and layout thrashing.