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 Cong | Mô Tả |
|---|---|---|
| ease | Tăng tốc nhanh, chậm dần | Mặc định, tốt cho hầu hết UI |
| Nút, thẻ, hover | linear | Tốc độ không đổi |
| Máy móc, hiếm khi tự nhiên | Spinner, thanh tiến trình | ease-in |
| Bắt đầu chậm, kết thúc nhanh | Di chuyển ra khỏi màn hình | Phần tử rời đi |
| ease-out | Bắt đầu nhanh, kết thúc chậm | Di chuyển vào màn hình |
| Phần tử xuất hiện | ease-in-out | Bắt đầu chậm, nhanh, kết thúc chậm |
| Chuyển động tự nhiên nhất | Hầu hết hoạt ảnh | cubic-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à.
- transition-property: Thuộc tính CSS cần transition (ví dụ: opacity, transform, background-color). Sử dụng all cho mọi thuộc tính có thể hoạt ảnh, nhưng điều này có thể ảnh hưởng đến hiệu suất.
- transition-duration: Thời gian để hoàn thành transition, tính bằng giây (s) hoặc mili giây (ms). Giá trị phổ biến: 0.2s đến 0.5s cho tương tác UI.
- transition-timing-function: Đường cong easing kiểm soát tốc độ thay đổi. Mặc định là ease.
- transition-delay: Thời gian chờ trước khi bắt đầu transition. Hữu ích để tạo hiệu ứng tuần tự.
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
- animation-name: Tên của @keyframes rule sẽ được áp dụng.
- animation-duration: Thời gian để hoàn thành một chu kỳ hoạt ảnh.
- animation-timing-function: Đường cong easing cho hoạt ảnh.
- animation-delay: Thời gian chờ trước khi bắt đầu hoạt ảnh.
- animation-iteration-count: Số lần lặp (số hoặc infinite).
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.
- animation-direction: Hướng phát (normal, reverse, alternate, alternate-reverse).
- animation-fill-mode: Cách hoạt ảnh áp dụng style trước/sau khi phát.
- animation-play-state: Kiểm soát phát/tạm dừng (running, paused).
- 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ụng | Thuộc Tính | Hoạt Ảnh Được? | Ghi Chú |
|---|---|---|---|
| Đường cong tùy chỉnh 4 điểm | Kiểm soát hoàn toàn | Easing tùy chỉnh | steps() |
| Bước rời rạc | Hiệu ứng stop-motion | Hoạt ảnh sprite | transform |
| Có | Chỉ compositor, hiệu suất tốt nhất | opacity | Có |
| Chỉ compositor | color | Có | Kích hoạt paint |
| background-color | Có | Kích hoạt paint | width |
| Có | Kích hoạt layout, tránh | height | Có |
| Kích hoạt layout, tránh | left/top | Có | Kích hoạt layout, dùng transform thay thế |
| display | Không | Không thể transition, dùng opacity + visibility | background-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.
transform: translateX(100px)left: 100pxtransform: translateY(50px)top: 50pxtransform: scale(1.5)width: 150%; height: 150%transform: rotate(45deg)instead of rotating via layout
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:
- Remove: Decorative animations, parallax scrolling, auto-playing carousels, pulsing effects, and looping background animations.
- Simplify: Transition durations should be shortened to near-instantaneous (0.01ms) rather than removed entirely, so state changes are still perceptible.
- Keep: Functional state transitions (hover, focus, active states), progress indicators, and any animation that conveys essential information.
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
- Animate.css: A collection of pre-built CSS animation classes. Simply add a class name to an element to apply an animation. Good for quick prototyping but can lead to generic-feeling motion if overused.
- Hamburgers: A collection of animated hamburger menu icon transitions, all implemented in pure CSS.
Hoạt Ảnh Tiến Trình Đọc
- Framer Motion: A React animation library that provides declarative animation APIs with spring physics, layout animations, and gesture support. The best choice for React applications.
- GSAP (GreenSock): The most powerful JavaScript animation library. Provides precise timeline control, complex easing, SVG morphing, and scroll-triggered animations. Used by major brands for marketing sites and interactive experiences.
- Anime.js: A lightweight JavaScript animation library with a clean API. Supports CSS properties, SVG, DOM attributes, and JavaScript objects. A good middle ground between CSS-only and GSAP.
- Motion One: A modern, lightweight animation library built on the Web Animations API. Provides a minimal API with spring physics and timeline support.
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:
- Timeline view: See all active animations on the page with their durations, delays, and keyframes.
- Playback controls: Slow animations down to 1/4 speed, pause, or scrub through the timeline frame by frame.
- Property inspection: Click on an animation in the timeline to see the exact CSS properties being animated and their values at each keyframe.
- Live editing: Modify animation duration, delay, and easing in real time without reloading the page.
Performance Profiling
Use the Performance panel in Chrome DevTools to identify animation jank:
- Record a performance trace while the animation is running
- Look for frames that exceed the 16.67ms budget (shown as red bars in the frame chart)
- Check for "Layout" operations during animation frames, which indicate layout-triggering properties
- Use the "Rendering" tab to enable "Paint flashing" (shows repainted areas in green) and "Layer borders" (shows compositor layers)
Common Debugging Scenarios
- Animation not running:
animation-fill-modeforwardsbothtransform: scale(0)makes the element invisible but still present. - Janky animation:
transformopacity. - Animation delay feels wrong:
animation-delayanimation-fill-mode: backwardsbothis set. - Animation works in Chrome but not Safari:
animationshorthand. Ensure all values are in the correct order and that you are not using unsupported features.
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 MinifierCô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.