- Sử dụng
clip-pathvàmask-imageđể hiển thị hình học tuỳ chỉnh và làm mờ dần một cách mượt mà. - Sử dụng Ảnh động dựa trên thao tác cuộn (
animation-timeline: scroll()) cho các hiệu ứng không thiết yếu dựa trên thao tác cuộn thay vì trình nghe JS. - Sử dụng Hiệu ứng chuyển đổi khung hiển thị để tạo ảnh động giữa các trạng thái bố cục phức tạp một cách liền mạch.
Hiệu suất
- Ưu tiên tạo ảnh động cho
opacityvàtransform(bao gồm cả các thuộc tính biến đổi riêng lẻ, ví dụ:translatethay vìleft/right/top/bottom) để đảm bảo ảnh động luôn nằm trên luồng trình tổng hợp. - Sử dụng
transition-behavior: allow-discrete,@starting-stylevà (đối với các phần tử lớp trên cùng như<dialog>hoặc[popover])overlayđể tạo ảnh động cho các trạng thái vào và thoát riêng biệt; xem bài viết Tạo ảnh động cho phần tử vào và thoát và Tạo ảnh động cho phần tử đến và đi từ lớp trên cùng.
.popover-reveal {
/* Transition discrete display and top-layer overlay alongside opacity */
transition:
opacity ease-out,
display,
overlay;
transition-duration: 0.2s;
transition-behavior: allow-discrete;
}
Hỗ trợ tiếp cận
Sử dụng truy vấn phương tiện prefers-reduced-motion để tắt chuyển động mạnh cho những người dùng muốn tắt.
- Cung cấp cơ chế tạm dừng: Cho phép người dùng dừng băng chuyền, biểu ngữ hoặc ảnh động liên tục khác tự động chạy.
- Mặc định là chế độ xem tĩnh: Cân nhắc đặt mặc định là trạng thái tĩnh và cho phép người dùng chọn sử dụng chuyển động.
- Không vượt quá giới hạn nhấp nháy (3 lần mỗi giây): Tuyệt đối không sử dụng hiệu ứng nhấp nháy nhanh từ sáng sang tối. Những hiệu ứng như vậy có thể gây ra cơn động kinh.
KHÔNG áp dụng animation-duration: 0.01ms; trên toàn cục vì việc này có thể khiến một số ảnh động trở nên khó chịu hơn.
Thay vào đó, hãy xử lý các lựa chọn ưu tiên về chuyển động theo từng trường hợp bằng một trong hai phương pháp, lồng truy vấn @media vào bên trong quy tắc để cả hai phiên bản được giữ cùng nhau:
- Bắt đầu với ảnh động được bật theo mặc định và giảm bớt ảnh động bằng
@media (prefers-reduced-motion: reduce):
/* Good: Dampen spin states or slow down animations for reduced motion */
.spinner {
animation: spin 1s linear infinite;
@media (prefers-reduced-motion: reduce) {
animation: none;
opacity: 0.5;
}
}
progress:not([value]) {
animation: slide 1s infinite linear;
@media (prefers-reduced-motion: reduce) {
animation-duration: 20s;
}
}
- Bắt đầu mà không có ảnh động theo mặc định và bật ảnh động bằng
@media (prefers-reduced-motion: no-preference):
/* Good: Default to static or gentle states and opt-in to full motion */
.spinner {
opacity: 0.5;
@media (prefers-reduced-motion: no-preference) {
opacity: 1;
animation: spin 1s linear infinite;
}
}
progress:not([value]) {
animation: slide 20s infinite linear;
@media (prefers-reduced-motion: no-preference) {
animation-duration: 1s;
}
}
Làm việc với các sự kiện ảnh động và chuyển đổi trong JavaScript
Trên cả giao diện AnimationEvent (animationstart, animationiteration, animationend, animationcancel) và TransitionEvent (transitionrun, transitionstart, transitionend, transitioncancel), event.target cung cấp cho bạn phần tử DOM gốc. Để truy cập vào đối tượng Animation cụ thể (Animation, CSSAnimation hoặc CSSTransition) đã kích hoạt sự kiện, hãy sử dụng thuộc tính event.animation chỉ có thể đọc.
Khi không có event.animation, bạn thường có thể tìm thấy thực thể Animation bằng cách gọi event.target.getAnimations() và lọc theo event.animationName (hoặc event.propertyName cho các hiệu ứng chuyển đổi). Tuy nhiên, bạn nên sử dụng trực tiếp event.animation để tránh một số trường hợp mà phương án dự phòng lọc event.target.getAnimations() này không hoạt động như mong đợi:
- Ảnh động của API Ảnh động web (
element.animate()) không cóanimationName, vì vậy, không thể so khớp theo tên khi lọcevent.target.getAnimations(). - Việc lọc theo
animationNamechoAnimationEventkhông rõ ràng khi nhiều ảnh động có cùng tên được đính kèm vào cùng một phần tử.- Xin lưu ý rằng việc lọc trên
transitionPropertychoTransitionEvents hoạt động vì chỉ có một sự kiện cho mỗi thuộc tính chuyển đổi.
- Xin lưu ý rằng việc lọc trên
event.targetluôn trỏ đến phần tử DOM gốc, vì vậy, thuộc tính này không phân biệt giữa ảnh động chạy trên chính phần tử đó và một trong các phần tử giả của phần tử đó, trong khievent.animation.effect.pseudoElementhiển thị trực tiếp phần tử giả mục tiêu.
// Works in most basic CSS animation cases, but fails for WAAPI animations,
// duplicate animation names on the same element, or pseudo-elements.
document.addEventListener('animationstart', (event) => {
const animation = event.target
.getAnimations()
.find((anim) => anim.animationName === event.animationName);
console.log(animation);
});
// PREFERRED: Directly access the exact Animation object via event.animation
document.addEventListener('animationstart', (event) => {
const animation = event.animation;
console.log(animation);
});
Bạn có thể phát hiện tính năng hỗ trợ bằng 'animation' in AnimationEvent.prototype (hoặc 'animation' in TransitionEvent.prototype).