Chỉ báo tiến trình cuộn là một mẫu giao diện người dùng phổ biến, giúp truyền đạt một cách trực quan tiến trình của người dùng thông qua một tài liệu hoặc vùng chứa có thể cuộn. Khi người dùng cuộn, một phần tử hình ảnh sẽ cập nhật để phản ánh vị trí của họ, giúp họ biết rõ và trực quan về lượng nội dung đã xem và lượng nội dung còn lại.
Cách triển khai
Để tạo chỉ báo tiến trình cuộn, bạn cần có hai thứ:
- Một phần tử đóng vai trò là thanh tiến trình. Phần tử này thường là
position: fixedhoặcposition: absoluteđể luôn hiển thị khi người dùng cuộn. - Ảnh động được liên kết với vị trí cuộn.
Sau đây là cách bạn có thể thực hiện việc này:
- Trước tiên, hãy tạo một phần tử HTML đóng vai trò là thanh tiến trình. Bạn có thể tạo kiểu cho phần tử này theo ý thích.
- Tiếp theo, trong CSS, hãy xác định ảnh động
@keyframesđiều chỉnh tỷ lệ thanh tiến trình. Một phương pháp phổ biến là điều chỉnh tỷ lệ phần tử từscaleX(0)thànhscaleX(1). - Cuối cùng, hãy áp dụng ảnh động này cho phần tử thanh tiến trình và đặt
animation-timelinecủa phần tử đó thành dòng thời gian cuộn. Thao tác này sẽ yêu cầu trình duyệt điều khiển tiến trình của ảnh động dựa trên vị trí cuộn của trình cuộn cấp trên gần nhất.
Mã mẫu
Mã này sẽ tăng phần tử #progress khi cuộn bằng cách sử dụng dòng thời gian cuộn ẩn danh, do hàm scroll() tạo.
@media (prefers-reduced-motion: no-preference) {
@supports ((animation-timeline: scroll())) {
@keyframes grow-progress {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
#progress {
position: fixed;
left: 0; top: 0;
width: 100%; height: 1em;
background: red;
transform-origin: 0 50%;
animation: grow-progress auto linear;
animation-timeline: scroll();
}
}
}
Do vị trí của hàm này trong DOM, hàm scroll() sẽ theo dõi trình cuộn đối tượng cấp trên gần nhất theo hướng block, trong trường hợp này là trình cuộn gốc.
<body>
<!-- MANDATORY: Purely decorative visual scroll progress bars MUST set aria-hidden="true" to remove the empty element from the assistive technology reading tree -->
<div id="progress" aria-hidden="true"></div>
</body>
Mã này tăng phần tử #progress khi cuộn bằng cách sử dụng dòng thời gian cuộn được đặt tên, do thuộc tính scroll-timeline tạo.
@media (prefers-reduced-motion: no-preference) {
@supports ((animation-timeline: scroll())) {
@keyframes grow-progress {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
:root {
scroll-timeline: --tl block;
}
#progress {
position: fixed;
left: 0; top: 0;
width: 100%; height: 1em;
background: red;
transform-origin: 0 50%;
animation: grow-progress auto linear;
animation-timeline: --tl;
}
}
}
Các phương pháp hay nhất
Khi sử dụng ảnh động dựa trên thao tác cuộn, bạn cần tuân thủ một số phương pháp hay nhất để đảm bảo trải nghiệm mượt mà và dễ tiếp cận:
- NÊN thêm tính năng phát hiện: Không phải trình duyệt nào cũng hỗ trợ ảnh động dựa trên thao tác cuộn. Sử dụng
@supports (animation-timeline: scroll())để kiểm tra khả năng hỗ trợ và cung cấp phương án dự phòng cho các trình duyệt không hỗ trợ.- KHÔNG sử dụng gói
scroll-timeline-polyfillcho chiến lược dự phòng vì gói này chưa hoàn thiện tính năng và có nhiều vấn đề đã biết. - Nếu ảnh động chỉ được coi là để trang trí, hãy chọn Progressive Enhancement (Cải tiến tăng dần) và KHÔNG cung cấp phương án dự phòng.
- KHÔNG sử dụng gói
- NÊN xoá các phần tử chỉ mang tính trang trí khỏi luồng đọc của công nghệ hỗ trợ: Áp dụng
aria-hidden="true"cho các chỉ báo cuộn chỉ mang tính trực quan để đảm bảo trình đọc màn hình không gặp phải các nút trống, không có tên. - NÊN tôn trọng lựa chọn ưu tiên của người dùng: Một số người dùng muốn có ít chuyển động hơn trên web. Sử dụng truy vấn nội dung nghe nhìn
prefers-reduced-motionđể tắt hoặc giảm ảnh động cho những người dùng này. - NÊN cố gắng chỉ tạo ảnh động cho các thuộc tính CSS hiệu suất cao: Để có ảnh động mượt mà nhất, hãy chỉ tạo ảnh động cho các thuộc tính có thể được xử lý bằng luồng trình tổng hợp của trình duyệt, chẳng hạn như
transformvàopacity. Việc tạo ảnh động cho các thuộc tính khác nhưwidthhoặcheightcó thể dẫn đến các vấn đề về hiệu suất. - NÊN sử dụng đúng thứ tự khai báo: Khi sử dụng thuộc tính viết tắt
animation, hãy khai báoanimation-timelinesau thuộc tính đó để ngăn thuộc tính viết tắt đặt lại dòng thời gian.
Khi sử dụng hàm scroll() để tạo ảnh động dựa trên thao tác cuộn:
- KHÔNG BẮT BUỘC nêu rõ về trình cuộn: Khi không nhắm đến trình cuộn cấp trên gần nhất, hãy nêu rõ trình cuộn mà bạn muốn sử dụng bằng
scroll(root)hoặcscroll(self).- Khi
root,nearesthoặcselfkhông đủ, hãy sử dụng dòng thời gian cuộn được đặt tên.
- Khi
- KHÔNG BẮT BUỘC nêu rõ trục cần theo dõi: Khi không nhắm đến trục
blockmặc định (chẳng hạn như trong trình cuộn ngang), hãy nêu rõ trục cần theo dõi bằngscroll(block)hoặcscroll(inline).
Khi sử dụng thuộc tính scroll-timeline để tạo ảnh động dựa trên thao tác cuộn:
- NÊN sử dụng
<dashed-ident>CSS cho tên. - KHÔNG BẮT BUỘC nêu rõ trục cần theo dõi: Khi không nhắm đến trục
blockmặc định (chẳng hạn như trong trình cuộn ngang), hãy nêu rõ trục cần theo dõi bằngscroll-timeline-axis. - NÊN đảm bảo phạm vi của hoạt động tra cứu hoạt động: Khi phần tử khai báo
scroll-timelinekhông phải là phần tử cấp trên dạng cây phẳng của phần tử ảnh động, hãy nâng chế độ hiển thị tên củascroll-timelinebằng cách sử dụngtimeline-scopetrên một phần tử cấp trên được chia sẻ.
Chiến lược dự phòng
Đối với những trình duyệt không hỗ trợ ảnh động dựa trên thao tác cuộn, bạn có thể sử dụng tính năng dự phòng để tạo lại hiệu ứng hình ảnh. Các phương án dự phòng thường được xây dựng bằng trình nghe cuộn (cho hiệu ứng ScrollTimeline) hoặc API IntersectionObserver (cho hiệu ứng ViewTimeline).
Trong các trình duyệt có hỗ trợ tích hợp cho ảnh động dựa trên thao tác cuộn, hãy LUÔN sử dụng phương thức triển khai CSS gốc vì các trình duyệt đó có hiệu suất cao hơn.
Xin lưu ý rằng không phải hiệu ứng nào cũng có thể được tạo lại bằng phương pháp dự phòng.
Cụ thể cho trường hợp sử dụng này, tập lệnh sau đây áp dụng phương án dự phòng cho các trình duyệt không hỗ trợ ảnh động dựa trên thao tác cuộn. Hàm này sử dụng trình nghe cuộn để theo dõi vị trí cuộn của phần tử gốc và cập nhật thuộc tính transform của thanh tiến trình cho phù hợp.
<script>
if (!CSS.supports('animation-timeline', 'scroll()')) {
const progress = document.querySelector('#progress');
window.addEventListener('scroll', () => {
const scrollable = document.documentElement.scrollHeight - window.innerHeight;
const scrolled = window.scrollY;
const progressPercentage = (scrolled / scrollable);
progress.style.transform = `scaleX(${progressPercentage})`;
});
}
</script>