Thêm hiệu ứng hiển thị tương tác, hiệu quả vào trang web của bạn bằng mặt nạ CSS và các thuộc tính tuỳ chỉnh đã đăng ký. Bằng cách sử dụng dải màu toả tròn làm mặt nạ và đăng ký các giá trị dừng, chúng ta có thể chuyển đổi mượt mà giữa trạng thái vào và ra, đồng thời theo dõi con trỏ của người dùng bằng JavaScript tối thiểu.
Triển khai
1. Đăng ký thuộc tính tuỳ chỉnh
Để cho phép nội suy mượt mà các giá trị điểm dừng chuyển màu, bạn phải đăng ký các biến bằng @property. Thao tác này sẽ thông báo cho công cụ của trình duyệt về loại dữ liệu, cho phép công cụ này chuyển đổi giữa các giá trị trong quá trình cập nhật.
/* Register the spotlight inner and outer sizes to enable interpolation */
@property --inner-size{
syntax: "<length-percentage>";
inherits: true;
initial-value: 0px;
}
@property --outer-size{
syntax: "<length-percentage>";
inherits: true;
initial-value: 0px;
}
Các thuộc tính tuỳ chỉnh theo dõi vị trí con trỏ không cần phải chuyển đổi, vì vậy bạn không cần phải đăng ký các thuộc tính đó.
2. Xác định lớp che giấu
Áp dụng mask-image cho phần tử bạn muốn hiển thị. Sử dụng radial-gradient tham chiếu đến các thuộc tính đã đăng ký.
.reveal-layer {
/* Only transition the size properties, NOT the position variables */
transition: --inner-size 0.2s ease-in-out, --outer-size 0.2s ease-in-out;
/* The spotlight is defined by the transparent center of the mask */
mask-image: radial-gradient(
circle at var(--mouse-x) var(--mouse-y),
black var(--inner-size, 0%),
transparent var(--outer-size, 0%)
);
/* Ensure the mask doesn't repeat if the element is large */
mask-repeat: no-repeat;
/* Make the mask layer non-interactive */
pointer-events: none;
}
/* Update the gradients stops on interaction */
.reveal-layer:hover {
--inner-size: 100px;
--outer-size: 120px;
}
3. Cập nhật toạ độ bằng JavaScript
Theo dõi vị trí con trỏ và cập nhật các biến CSS. Vì các thuộc tính được đăng ký và có transition được xác định, nên đèn chiếu sẽ di chuyển mượt mà ngay cả khi các sự kiện con trỏ không thường xuyên.
const container = document.querySelector('.container');
// Store the container's bounding rect
let rect = container.getBoundingClientRect();
// Update the rect when the container is resized
const resizeObserver = new ResizeObserver(()=>{
rect = container.getBoundingClientRect();
})
resizeObserver.observe(container);
container.addEventListener('pointermove', (e) => {
// Calculate position as a percentage of the container.
const x = ((e.clientX - rect.left) / rect.width) * 100;
const y = ((e.clientY - rect.top) / rect.height) * 100;
// Update the registered properties
container.style.setProperty('--mouse-x', `${x}%`);
container.style.setProperty('--mouse-y', `${y}%`);
});
4. Hỗ trợ tiếp cận và tương tác
Đảm bảo hỗ trợ tiếp cận BẮT BUỘC: Mẫu này dựa vào các lượt tương tác bằng con trỏ để hiển thị tiêu điểm trực quan. Bạn PHẢI đảm bảo rằng tất cả nội dung cơ bản vẫn hiển thị đầy đủ, dễ đọc và có thể truy cập độc lập bằng bàn phím theo mặc định trong bố cục cơ bản, chỉ sử dụng lớp tiêu điểm làm tính năng nâng cao hình ảnh không cần thiết cho người dùng con trỏ. Đừng bao giờ sử dụng hiệu ứng này để che khuất hoặc chặn nội dung thiết yếu đối với người dùng chỉ sử dụng bàn phím hoặc công nghệ hỗ trợ.
- Sự kiện con trỏ: Đặt
pointer-events: nonetrên lớp phủ mặt nạ để cho phép các lượt tương tác nhấp và chạm chuẩn truyền đến các thành phần điều khiển bên dưới. - Ghi đè giảm chuyển động: Tắt nội suy chuyển đổi mượt mà cho người dùng yêu cầu giảm chuyển động.
/* MANDATORY Copy-Paste Safety: Disable transition scaling for motion-sensitive users */
@media (prefers-reduced-motion: reduce) {
.reveal-layer {
transition: none !important;
}
}
Chiến lược dự phòng
Dự phòng cho tài sản chưa đăng ký
Các trình duyệt hỗ trợ mask-image nhưng không hỗ trợ @property vẫn sẽ hiển thị hiệu ứng đèn chiếu, nhưng chuyển động sẽ nhảy giữa trạng thái bật và tắt vì các trình duyệt này không thể nội suy các giá trị bên trong radial-gradient. Cung cấp giá trị dự phòng khi sử dụng var().
.reveal-layer {
mask-image: radial-gradient(
circle at var(--mouse-x) var(--mouse-y),
/* Use fallback values when using the `var()` function for browsers that don't get an initial value from the @property registration. */
black var(--inner-size, 0%),
transparent var(--outer-size, 0%)
);
}
Hỗ trợ mặt nạ cơ bản
Đối với các trình duyệt hoàn toàn không hỗ trợ tính năng tạo mặt nạ CSS:
- Thuộc tính có tiền tố: Sử dụng thuộc tính có tiền tố
-webkit-mask-imageđể hỗ trợ trình duyệt rộng hơn. - Cải tiến tăng dần: Thiết kế trạng thái cơ bản của giao diện người dùng để có đầy đủ chức năng và dễ đọc mà không cần hiệu ứng hiển thị. Điều này rất hữu ích khi hiệu ứng chỉ thêm hiệu ứng hình ảnh chứ không phải là yêu cầu để đọc nội dung.