Hiệu ứng hover “nam châm” cho lưới bento, gần như chỉ bằng CSS
Lưới bento là kiểu bố cục gồm nhiều ô to nhỏ khác nhau xếp cạnh nhau. Một hiệu ứng đẹp cho nó: khi rê chuột, một khung viền “hút” theo con trỏ, trượt sang ô đang được hover và co giãn đúng bằng kích thước ô đó. Trước đây việc này thường cần JavaScript để đo toạ độ bằng getBoundingClientRect(). Với CSS anchor positioning, phần lõi chỉ còn vài dòng.
Toàn bộ mẹo
.bento {
li:hover article { anchor-name: --a; }
.magnet {
position-anchor: --a;
inset: anchor(inside);
transition: inset var(--dur) var(--ease);
}
}Về bản chất, đó là những dòng cần có để tạo hiệu ứng. Phần còn lại chỉ là trang trí: màu, bo góc, độ mờ khi không hover.
Vì sao nó chạy được
Tên anchor động. Thay vì gán cố định anchor-name cho một phần tử, ta chỉ gán nó cho ô đang được hover: li:hover article { anchor-name: --a }. Rê sang ô khác thì tên --a chuyển theo.
Khung chỉ báo bám theo tên đó. .magnet là một phần tử định vị tuyệt đối, khai báo position-anchor: --a. Với inset: anchor(inside), cạnh trên khớp cạnh trên của anchor, cạnh trái khớp cạnh trái, và tương tự cho hai cạnh còn lại. Khung chỉ báo vì thế luôn phủ đúng ô đang hover, dù ô đó to hay nhỏ.
Chuyển động. Khi anchor đổi, giá trị inset đổi theo, và transition: inset biến cú nhảy thành một cú trượt. Hai biến --dur và --ease cho phép tinh chỉnh cảm giác.
Ví dụ 1: Lưới bento
Rê chuột qua các ô. Ví dụ này không có một dòng JavaScript nào. Khi con trỏ rời khỏi lưới, khung chỉ báo mờ đi nhờ .bento:has(li:hover).
Ví dụ 2: Tinh chỉnh chuyển động
Cùng kỹ thuật, áp dụng cho một thanh điều hướng. Kéo thanh trượt để đổi --dur, chọn đường cong để đổi --ease. Có thể thử những đường cong “nảy” hơn, nhưng một cubic-bezier đơn giản thường cho cảm giác gọn và dễ chịu nhất.
Ví dụ 3: Phương án dự phòng bằng JavaScript
Với trình duyệt chưa hỗ trợ anchor positioning, có thể dùng một phép kiểm tra hỗ trợ cộng với một listener con trỏ đặt trên khung chứa, theo kiểu event delegation: đo ô đang hover rồi ghi toạ độ vào biến CSS. Bật công tắc để ép ví dụ chạy theo đường JS và so sánh.
Lưu ý khi áp dụng
- Đặt
.magnetsau lưới trong HTML và cùng nằm trong khung chứa cóposition: relative, để nó tìm được anchor. - Nếu lưới dùng
gap, lúc con trỏ đi qua khe hở sẽ không có ô nào được hover và khung chỉ báo mất anchor. Các ví dụ ở trên tạo khoảng cách bằngpaddingtrong từngliđể tránh chuyện này. - Firefox hiện chưa hỗ trợ animate vị trí tính bằng
anchor(). Khung chỉ báo vẫn nhảy đúng chỗ nhưng không trượt. Muốn có chuyển động ở đó thì phải tự làm hiệu ứng bằng JavaScript, ví dụ theo kỹ thuật FLIP hoặc như đường JS ở ví dụ 3. - Hover không có ý nghĩa trên màn hình cảm ứng. Với thiết bị không có con trỏ chính xác, nên cân nhắc một thiết kế khác, chẳng hạn ở màn hình hẹp thì cho khung chỉ báo di chuyển giữa các ô theo thao tác cuộn.
- Các ví dụ tắt chuyển động trượt khi người dùng bật
prefers-reduced-motion, chỉ giữ hiệu ứng mờ.
Hỗ trợ trình duyệt. Kỹ thuật dựa vào CSS anchor positioning. Hãy kiểm tra caniuse.com/css-anchor-positioning trước khi dùng cho sản phẩm thật, và giữ phương án JS ở ví dụ 3 cho những trình duyệt còn thiếu.