CSS transition và animation: thêm chuyển động mượt cho giao diện

  • author-image

    Nguyễn Đình Cường

  • blog-tag
  • blog-comment 0 comment
  • created-date 09 Nov, 2026
blog-thumbnail

Một chút chuyển động đúng chỗ khiến giao diện sống động và dễ hiểu hơn: nút đổi màu khi rê chuột, menu trượt ra, thông báo mờ dần. Bạn không cần thư viện nặng nề, CSS đã có sẵn hai công cụ cho việc này là transition và animation.

Transition: chuyển mượt giữa hai trạng thái

Transition làm cho sự thay đổi của một thuộc tính diễn ra từ từ thay vì tức thì. Bạn khai báo ở trạng thái gốc:

.button {
  background: #2563eb;
  transform: translateY(0);
  transition: background 0.2s ease, transform 0.2s ease;
}
.button:hover {
  background: #1d4ed8;
  transform: translateY(-2px);
}

Cú pháp gồm tên thuộc tính, thời lượng, hàm tốc độ và có thể thêm độ trễ. Hàm tốc độ phổ biến là ease, ease-in-out, linear. Hãy khai báo transition ở trạng thái gốc, không phải ở :hover, để hiệu ứng chạy cả lúc vào và lúc ra.

Đừng dùng transition: all bừa bãi. Nó có thể làm chuyển động những thuộc tính bạn không định, và gây tốn tài nguyên. Hãy liệt kê rõ các thuộc tính cần chuyển.

Animation với keyframes

Khi cần chuyển động phức tạp hơn hoặc tự chạy mà không cần sự kiện, dùng @keyframes:

@keyframes fade-in-up {
  from {
    opacity: 0;
    transform: translateY(16px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.toast {
  animation: fade-in-up 0.4s ease-out both;
}

Bạn định nghĩa các mốc (từ from đến to, hoặc theo phần trăm như 0%, 50%, 100%) rồi gắn vào phần tử qua thuộc tính animation. Một số thuộc tính con đáng biết:

  • animation-duration: thời lượng một vòng.
  • animation-iteration-count: số lần lặp, hoặc infinite để lặp mãi.
  • animation-delay: độ trễ trước khi chạy.
  • animation-fill-mode: both giữ trạng thái đầu trước khi chạy và trạng thái cuối sau khi chạy.
  • animation-direction: chiều chạy, ví dụ alternate để chạy xuôi rồi ngược.

Ví dụ: vòng quay tải

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

.spinner {
  width: 32px;
  height: 32px;
  border: 4px solid #cbd5e1;
  border-top-color: #2563eb;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

Chỉ với vài dòng, bạn có một biểu tượng đang tải, không cần ảnh hay thư viện.

Chọn thuộc tính nào để chuyển động?

Không phải thuộc tính nào cũng rẻ như nhau. Khi bạn thay đổi width, height, margin hay top, trình duyệt thường phải tính lại bố cục của cả trang, việc này tốn kém và dễ gây giật. Hai thuộc tính transform và opacity thường được trình duyệt xử lý hiệu quả hơn nhiều, có thể giao cho GPU. Vì vậy:

  1. Muốn di chuyển: dùng transform: translate() thay vì đổi left hoặc top.
  2. Muốn phóng to hoặc thu nhỏ: dùng transform: scale().
  3. Muốn hiện hoặc ẩn: dùng opacity.

Tôn trọng người dùng nhạy cảm với chuyển động

Một số người bị chóng mặt hoặc khó chịu khi gặp hiệu ứng chuyển động mạnh, và họ có thể bật tùy chọn "giảm chuyển động" trong hệ điều hành. Bạn có thể tôn trọng điều này bằng media query:

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

Đây là một trong số ít trường hợp !important được chấp nhận vì mục đích khả năng tiếp cận.

Nguyên tắc dùng chuyển động có chừng mực

  • Thời lượng thường nên ngắn, khoảng 150 đến 400 mili giây cho hiệu ứng giao diện.
  • Chuyển động nên có mục đích: báo trạng thái, hướng sự chú ý, giải thích mối quan hệ giữa các phần tử.
  • Tránh hiệu ứng lặp vô hạn ở nơi người dùng đang cần đọc.
  • Kiểm tra trên thiết bị yếu để chắc hiệu ứng vẫn mượt.

Ví dụ: thẻ nổi lên khi rê chuột

Một hiệu ứng rất thường gặp trên giao diện danh sách khóa học hay sản phẩm là thẻ nhấc lên nhẹ và đổ bóng đậm hơn:

.card {
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.card:hover,
.card:focus-within {
  transform: translateY(-4px);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.18);
}

Hãy để ý :focus-within: nó áp dụng hiệu ứng khi một phần tử bên trong thẻ nhận focus bàn phím, giúp người dùng không dùng chuột cũng nhận được phản hồi tương tự. Đây là một thói quen nhỏ nhưng tạo khác biệt cho khả năng tiếp cận. Đừng quên rằng màn hình cảm ứng không có hover thực sự, nên hiệu ứng không bao giờ nên là cách duy nhất để truyền đạt thông tin quan trọng.

Kết luận

Transition dành cho thay đổi giữa hai trạng thái, animation dành cho chuỗi chuyển động phức tạp hoặc tự chạy. Ưu tiên transform và opacity, tôn trọng prefers-reduced-motion, và đừng lạm dụng.

Nếu bạn thích làm giao diện sinh động và muốn học có lộ trình rõ ràng, hãy xem các khóa học trên codebem.com để chọn khóa phù hợp.

author_photo
Nguyễn Đình Cường

0 comment