Master CSS Keyframes, control animation looping and boundaries with iteration-count and fill-mode, and learn classic Sprite Sheet animations using the discrete steps() timing function.
This lesson is currently only available in Vietnamese. Please switch the language toggle in the menu to Vietnamese to read the full guide and take the interactive quiz.
Ở bài học trước, chúng ta đã tìm hiểu về CSS Transition để tạo hiệu ứng chuyển tiếp mượt mà giữa hai
trạng thái (A sang B). Tuy nhiên, Transition có giới hạn rất lớn: nó bắt buộc phải có một tác nhân
kích hoạt (như :hover hoặc thay đổi class bằng JavaScript) và chỉ hỗ trợ hoạt ảnh 2 trạng
thái tuyến tính.
Để xây dựng các hiệu ứng chuyển động tự chạy ngay khi tải trang, lặp đi lặp lại vô hạn, hoặc di chuyển qua nhiều điểm mốc phức tạp (A -> B -> C -> D), trình duyệt cung cấp cho chúng ta bộ công cụ mạnh mẽ hơn: CSS Keyframes & Animations.
1. Khai báo hoạt ảnh đa trạng thái với @keyframes
Quy tắc @keyframes định nghĩa dòng thời gian của hoạt ảnh bằng cách chia thời lượng từ
0% (hoặc from) đến 100% (hoặc to) thành các điểm
mốc phần trăm. Tại mỗi mốc, bạn chỉ định các thuộc tính CSS mong muốn thiết lập:
@keyframes square-path {
0% { transform: translate(0, 0); }
25% { transform: translate(120px, 0); }
50% { transform: translate(120px, 60px); }
75% { transform: translate(0, 60px); }
100% { transform: translate(0, 0); }
}
2. Các thuộc tính điều khiển hoạt ảnh (CSS Animation Properties)
Sau khi đã định nghĩa xong @keyframes, ta gán nó vào phần tử mục tiêu bằng các thuộc tính sau:
- animation-name: Tên của
@keyframesđã tạo. -
animation-duration: Tổng thời gian hoàn thành 1 chu kỳ (ví dụ:
2s). -
animation-iteration-count: Số lần lặp. Nhận giá trị số nguyên hoặc
infinite(lặp vô tận). -
animation-direction: Hướng chạy (
normal,reverse- ngược,alternate- chạy xuôi rồi chạy ngược,alternate-reverse). -
animation-play-state: Trạng thái chạy (
running) hoặc tạm dừng (paused). - animation-fill-mode: Trạng thái của phần tử khi hoạt ảnh chưa bắt đầu hoặc đã kết thúc. Đây là thuộc tính rất quan trọng cần tìm hiểu kỹ.
none), khi hoạt ảnh kết thúc, phần tử sẽ ngay lập tức
giật ngược trở lại trạng thái ban đầu khi chưa chạy hoạt ảnh.
-
forwards: Giữ lại giao diện của điểm mốc cuối cùng (100%hoặcto) sau khi kết thúc. -
backwards: Áp dụng giao diện của điểm mốc đầu tiên (0%hoặcfrom) ngay lập tức khi hoạt ảnh đang chờ (delay), trước khi chạy. -
both: Áp dụng cả hai quy tắc trên (giữ 0% trong lúc delay, giữ 100% khi kết thúc).
3. Hàm Timing steps() & Nghệ thuật Sprite Animation
Thông thường, các hàm timing như linear hay cubic-bezier() nội suy liên tục
tạo cảm giác chuyển động mượt mà. Tuy nhiên, trong một số trường hợp, bạn lại muốn chuyển động diễn ra
dưới dạng các khung hình rời rạc (như kim giây đồng hồ nhảy giật cục, hoặc hoạt ảnh
chạy bộ từ một tệp ảnh Sprite Sheet của nhân vật game).
Hàm steps(n, position) chia hoạt ảnh thành $n$ bước nhảy bằng nhau. Cú pháp cơ bản:
steps(n, end) hoặc steps(n, start).
Ví dụ, một Sprite sheet gồm 4 khung hình nằm ngang, ta dịch chuyển background-position theo 3 bước nhảy:
.sprite-character {
width: 48px;
height: 48px;
background-image: url('sprite-sheet.png');
animation: play-sprite 0.8s steps(3, end) infinite;
}
@keyframes play-sprite {
from { background-position: 0px 0px; }
to { background-position: -144px 0px; } /* 3 bước nhảy: -48px, -96px, -144px */
}
Sân chơi tương tác: Keyframe & Animation Lab
Sân chơi dưới đây giúp bạn thực hành trực quan các cấu hình của Animation. Bạn có thể thay đổi các giá trị điều khiển để quan sát sự thay đổi trạng thái của hộp và chuyển động Sprite chạy bộ bằng Emojis:
Thiết lập Animation
steps(3)<div class="container">
<!-- Demo 1: Di chuyển -->
<div class="box animation-box">Box</div>
<!-- Demo 2: Sprite 48px viewport, 192px strip -->
<div class="sprite-viewport">
<div class="sprite-strip">
<div class="frame">🚶</div>
<div class="frame">🏃</div>
<div class="frame">🏃♂️</div>
<div class="frame">🚶</div>
</div>
</div>
</div>
4. Chuỗi hoạt ảnh (Chaining) & Tối ưu hóa hiệu năng
Bạn có thể kích hoạt nhiều animation song song trên cùng một phần tử bằng cách phân tách chúng bằng dấu phẩy:
.element {
/* Chạy đồng thời hoạt ảnh di chuyển 2 giây và xoay 1 giây vô tận */
animation: move-x 2s forwards, spin 1s linear infinite;
}
Khi làm việc với hoạt ảnh phức tạp, hãy nhớ quy tắc vàng: Chỉ làm animation với transform và opacity để trình duyệt xử lý trực tiếp trên GPU (Compositor Thread), ngăn ngừa hiện tượng giật giật (Jank) do reflow liên tục gây ra.
Trắc nghiệm ôn tập
Câu 1: Giá trị nào của animation-fill-mode giúp phần tử GIỮ LẠI trạng thái CSS của khung hình cuối cùng (100%) sau khi kết thúc hoạt ảnh?
Trắc nghiệm ôn tập
Câu 2: Nếu muốn chạy hoạt ảnh Sprite sheet gồm 5 khung hình liên tiếp, bạn cần sử dụng hàm steps() với tham số nào?