Menu

Animation trong CSS: @keyframes, thuộc tính và ví dụ

Animation trong CSS đưa một phần tử đi qua các bước bạn định nghĩa trong @keyframes, tự chạy và lặp bao nhiêu lần tùy ý. Tìm hiểu cú pháp @keyframes, cả tám thuộc tính animation, fill-mode và direction, hiệu ứng loading, tạm dừng và cách tôn trọng tùy chọn giảm chuyển động.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Animation CSS qua một ví dụ

Một animation gồm hai phần: @keyframes mô tả các bước, và thuộc tính animation áp dụng các bước đó cho phần tử với một thời lượng. Tấm thẻ này trượt vào và hiện dần khi trang tải xong:

Sửa code (hoặc đổi thời lượng thành 2s) là bản xem trước tải lại, để bạn xem tấm thẻ trượt vào thêm lần nữa. Khối @media (prefers-reduced-motion: reduce) thay keyframes bằng hiệu ứng hiện dần, chỉ dành cho người đọc có hệ thống được đặt chế độ giảm chuyển động.

Cú pháp @keyframes

@keyframes pulse {
  0%   { transform: scale(1); }
  50%  { transform: scale(1.08); }
  100% { transform: scale(1); }
}
  • from tương đương 0% và to tương đương 100%.
  • Thêm bao nhiêu bước phần trăm tùy ý. Các bước có cùng style có thể viết chung một dòng: 0%, 100% { ... }.
  • Thuộc tính nào thiếu ở keyframe đầu hoặc cuối sẽ chuyển động từ hoặc về giá trị gốc của phần tử.
  • Tên (pulse) là giá trị bạn truyền vào animation-name. Tên phân biệt chữ hoa chữ thường và không được trùng từ khóa như none.

Các thuộc tính animation

Thuộc tínhĐặt gìMặc định
animation-name@keyframes nào sẽ chạynone
animation-durationđộ dài một chu kỳ0s
animation-timing-functionđường cong tốc độ giữa các keyframeease
animation-delaythời gian chờ trước chu kỳ đầu (giá trị âm bắt đầu từ giữa chừng)0s
animation-iteration-countsố chu kỳ, hoặc infinite1
animation-directionnormal, reverse, alternate, alternate-reversenormal
animation-fill-modestyle trước và sau: none, forwards, backwards, bothnone
animation-play-staterunning hoặc pausedrunning

Cách viết rút gọn nhận các giá trị theo gần như mọi thứ tự. Quy tắc duy nhất: giá trị thời gian đầu tiên là thời lượng, giá trị thứ hai là thời gian chờ:

.loader {
  animation: pulse 1.2s ease-in-out 0.2s infinite alternate both;
  /*         name  duration timing  delay count   direction fill */
}

direction và fill-mode, xem trực tiếp

Chọn hướng, fill mode và số lần, rồi nhấn Play. Chiếc hộp di chuyển 200px đến cuối đường chạy. Dòng kết quả cho biết nó dừng ở đâu sau khi animation kết thúc:

Những điều cần để ý:

  • fill-mode: none (mặc định): sau khung hình cuối, hộp nhảy về x = 0 và trở lại màu hổ phách.
  • forwards: hộp giữ nguyên ở keyframe cuối. Với normal đó là x = 200 và màu xanh lá; với reverse hộp dừng ở x = 0 và màu xanh dương, vì keyframe chạy sau cùng là from.
  • alternate với số lần là 2 sẽ đi rồi về, nên ngay cả forwards cũng dừng ở x = 0.

Lặp vô hạn: hiệu ứng loading

infinite lặp mãi mãi, và animation-delay trên từng phần tử con làm chúng lệch nhau để lần lượt chuyển động. Giữ vòng lặp nhẹ nhàng: chậm, nhỏ, không nhấp nháy.

role="status" cùng aria-label cho người dùng trình đọc màn hình biết có thứ đang tải, điều mà riêng các dấu chấm không nói được. Với vòng tròn xoay, xem trang rotate.

Tạm dừng và điều khiển animation

animation-play-state: paused đóng băng animation ngay tại chỗ, và running cho nó chạy tiếp từ đó. Di chuột lên thanh bên dưới để tạm dừng:

Từ JavaScript, element.getAnimations() trả về các animation CSS đang chạy, mỗi cái có pause(), play(), cancel() và currentTime để đọc hoặc đặt. Sự kiện animationend được kích hoạt khi một animation không lặp vô hạn kết thúc, còn animationiteration ở cuối mỗi chu kỳ.

Tôn trọng giảm chuyển động và không bao giờ nhấp nháy

Có hai quy tắc trợ năng áp dụng cho mọi animation:

  1. Giảm chuyển động. Có người yêu cầu hệ thống giảm chuyển động vì chuyển động làm họ khó chịu. Cách an toàn nhất là chỉ thêm chuyển động khi họ không yêu cầu giảm:
@media (prefers-reduced-motion: no-preference) {
  .hero-title { animation: slide-in 0.8s ease-out both; }
}
  1. Không nhấp nháy. Nội dung nhấp nháy quá ba lần trong một giây có thể gây co giật. Hãy tránh hoàn toàn kiểu nhấp nháy nhanh, tương phản cao.

Giữ animation mượt

Hãy tạo animation cho transform và opacity khi có thể. Trình duyệt áp dụng được chúng mà không phải dàn trang lại, nên chúng vẫn mượt ngay cả trên điện thoại chậm. Tạo animation cho width, height, top, left hoặc margin sẽ tính lại bố cục ở mỗi khung hình. Để di chuyển phần tử, dùng translate() trong trang transform.

Lỗi thường gặp

  • Không có thời lượng. animation: fade; không làm gì cả, vì thời lượng mặc định là 0s.
  • Tên không khớp. animation-name phải khớp chính xác tên @keyframes, kể cả chữ hoa chữ thường.
  • Nhảy về lúc kết thúc. Thêm forwards (hoặc both) để giữ keyframe cuối.
  • Chớp hình trước khi animation có delay bắt đầu. Trong thời gian animation-delay, phần tử hiển thị style bình thường. backwards (hoặc both) áp dụng keyframe đầu tiên trong thời gian chờ.
  • Khởi động lại animation bằng cách đặt cùng giá trị. Đặt lại đúng giá trị animation cũ không có tác dụng. Hãy gỡ nó, ép trình duyệt reflow (el.offsetWidth), rồi đặt lại, như nút Play ở trên làm.
  • Tạo animation cho display. Thuộc tính này chuyển đổi ngay lập tức. Hãy tạo animation cho opacity và transform.

Câu hỏi thường gặp

Làm sao tạo animation trong CSS?

Định nghĩa các bước bằng @keyframes name { from { ... } to { ... } }, rồi áp dụng cho một phần tử bằng thuộc tính animation kèm thời lượng: animation: name 1s ease-out;. Thời lượng là bắt buộc, vì giá trị mặc định của nó là 0s.

animation-fill-mode: forwards dùng để làm gì?

Nó giữ lại style của keyframe cuối cùng sau khi animation kết thúc. Nếu không có nó, phần tử sẽ nhảy về style bình thường. backwards áp dụng keyframe đầu tiên trong thời gian chờ (delay), còn both làm cả hai.

Làm sao cho animation CSS lặp vô hạn?

Đặt animation-iteration-count: infinite, hoặc thêm infinite vào cách viết rút gọn: animation: spin 1s linear infinite;. Thêm alternate để mỗi chu kỳ chạy xuôi rồi chạy ngược.

Animation và transition trong CSS khác nhau thế nào?

Transition tạo chuyển động cho một thuộc tính giữa hai giá trị khi nó thay đổi, thường là khi hover hoặc khi đổi class. Animation tự chạy ngay khi được áp dụng, có thể có bao nhiêu keyframe cũng được, và có thể lặp lại, chạy ngược và tạm dừng.

Làm sao tạm dừng animation CSS?

Đặt animation-play-state: paused cho phần tử, ví dụ trong :hover hoặc qua một class được JavaScript thêm vào. Đặt lại thành running thì animation chạy tiếp từ chỗ đã dừng.

Làm sao tắt animation cho người dùng muốn giảm chuyển động?

Bọc animation trong @media (prefers-reduced-motion: no-preference) { ... }, hoặc gỡ nó bên trong @media (prefers-reduced-motion: reduce) { ... }. Truy vấn này theo cài đặt giảm chuyển động trong hệ điều hành của người dùng.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu