Cuộn được kiểm soát tốt bằng CSS Scroll Snap

Tạo trải nghiệm cuộn được kiểm soát tốt bằng cách khai báo các vị trí cố định khi cuộn.

Tính năng CSS Scroll Snap (Cố định vị trí cuộn CSS) cho phép nhà phát triển web tạo ra trải nghiệm cuộn được kiểm soát chặt chẽ bằng cách khai báo các vị trí cố định khi cuộn. Bài viết được phân trang và băng chuyền hình ảnh là hai ví dụ thường dùng cho trường hợp này. CSS Scroll Snap cung cấp một API dễ sử dụng và nhất quán để xây dựng các mẫu trải nghiệm người dùng phổ biến này.

Thông tin khái quát

Trường hợp cố định vị trí cuộn

Thao tác cuộn là một cách phổ biến và tự nhiên để tương tác với nội dung trên web. Đây là phương tiện gốc của nền tảng để cung cấp quyền truy cập vào nhiều thông tin hơn so với thông tin hiển thị trên màn hình cùng một lúc, trở nên đặc biệt quan trọng trên các nền tảng di động có không gian màn hình hạn chế. Do đó, không có gì ngạc nhiên khi các tác giả web ngày càng thích sắp xếp nội dung thành danh sách phẳng có thể cuộn thay vì phân cấp sâu.

Hạn chế chính của thao tác cuộn là thiếu độ chính xác. Thao tác cuộn hiếm khi kết thúc ở vị trí căn chỉnh với một đoạn văn hoặc câu. Điều này thậm chí còn rõ ràng hơn đối với nội dung được phân trang hoặc được chia thành từng mục có ranh giới rõ ràng khi thao tác cuộn kết thúc ở giữa trang hoặc hình ảnh, khiến nội dung chỉ hiển thị một phần. Những trường hợp sử dụng này được hưởng lợi từ trải nghiệm cuộn được kiểm soát tốt.

Các nhà phát triển web từ lâu đã dựa vào các giải pháp dựa trên JavaScript để kiểm soát thao tác cuộn nhằm giúp giải quyết thiếu sót này. Tuy nhiên, các giải pháp dựa trên JavaScript không cung cấp được giải pháp có độ chân thực cao do thiếu các dữ liệu gốc tuỳ chỉnh thao tác cuộn hoặc quyền truy cập vào thao tác cuộn kết hợp. Tính năng Cố định vị trí cuộn CSS đảm bảo một giải pháp nhanh, có độ chân thực cao và dễ sử dụng, hoạt động nhất quán trên các trình duyệt.

Tính năng Cố định vị trí cuộn CSS cho phép tác giả web đánh dấu từng vùng chứa cuộn bằng các ranh giới để hoàn tất thao tác cuộn. Sau đó, trình duyệt sẽ chọn vị trí kết thúc phù hợp nhất tuỳ thuộc vào thông tin cụ thể của thao tác cuộn, bố cục và chế độ hiển thị của vùng chứa cuộn, cũng như thông tin chi tiết về các vị trí chụp nhanh, sau đó tạo ảnh động mượt mà cho vị trí đó. Quay lại ví dụ trước, khi người dùng cuộn xong băng chuyền, hình ảnh hiển thị sẽ khớp vào vị trí. JavaScript không cần điều chỉnh thao tác cuộn.

Ví dụ về cách sử dụng tính năng cố định vị trí cuộn CSS với băng chuyền hình ảnh.
Ví dụ về cách sử dụng tính năng cố định vị trí cuộn CSS với băng chuyền hình ảnh. Tại đây, tính năng cố định vị trí cuộn đảm bảo rằng khi kết thúc thao tác cuộn, tâm ngang của hình ảnh sẽ được căn chỉnh với tâm ngang của vùng chứa cuộn.

Cố định vị trí cuộn bằng CSS

Cố định vị trí cuộn là thao tác điều chỉnh độ lệch cuộn của vùng chứa cuộn để ở vị trí cố định ưu tiên sau khi thao tác cuộn kết thúc.

Bạn có thể chọn cố định vị trí cuộn cho một vùng chứa cuộn bằng cách sử dụng thuộc tính scroll-snap-type. Điều này cho trình duyệt biết rằng trình duyệt nên xem xét việc điều chỉnh vùng chứa cuộn này vào các vị trí điều chỉnh do các thành phần con của vùng chứa tạo ra. scroll-snap-type xác định trục mà thao tác cuộn diễn ra: x, y hoặc both và độ nghiêm ngặt của thao tác bám dính: mandatory, proximity. Chúng ta sẽ tìm hiểu thêm về các tính năng này ở phần sau.

Bạn có thể tạo vị trí đính bằng cách khai báo một căn chỉnh mong muốn trên một phần tử. Vị trí này là độ dời cuộn mà tại đó vùng chứa cuộn cấp trên gần nhất và phần tử được căn chỉnh như đã chỉ định cho trục đã cho. Bạn có thể căn chỉnh theo các cách sau trên mỗi trục: start, end, center.

Căn chỉnh start có nghĩa là cạnh bắt đầu của điểm bắt dính vùng chứa cuộn phải được căn thẳng hàng với cạnh bắt đầu của vùng bắt dính phần tử. Tương tự, các cách căn chỉnh end và center có nghĩa là cạnh cuối hoặc tâm của điểm bắt dính vùng chứa cuộn phải được căn thẳng hàng với cạnh cuối hoặc tâm của vùng bắt dính phần tử.

Ví dụ về nhiều cách căn chỉnh trên trục cuộn ngang.

Các ví dụ sau đây minh hoạ cách sử dụng các khái niệm này.

Một trường hợp sử dụng phổ biến cho tính năng cố định vị trí cuộn là băng chuyền hình ảnh. Ví dụ: để tạo một băng chuyền hình ảnh ngang bám vào từng hình ảnh khi bạn di chuyển, chúng ta có thể chỉ định vùng chứa cuộn có scroll-snap-type bắt buộc trên trục ngang. đặt mỗi hình ảnh thành scroll-snap-align: center để đảm bảo rằng việc bám vào sẽ căn giữa hình ảnh trong băng chuyền.

#gallery {
  scroll-snap-type: x mandatory;
  overflow-x: scroll;
  display: flex;
}

#gallery img {
   scroll-snap-align: center;
}
<div id="gallery">
  <img src="cat.jpg">
  <img src="dog.jpg">
  <img src="another_cute_animal.jpg">
</div>

Vì các vị trí chụp nhanh được liên kết với một phần tử, nên thuật toán chụp nhanh có thể thông minh về thời điểm và cách chụp nhanh dựa trên phần tử và kích thước vùng chứa cuộn. Ví dụ: hãy xem xét trường hợp một hình ảnh lớn hơn băng chuyền. Thuật toán điều chỉnh nhanh đơn giản có thể ngăn người dùng kéo xung quanh để xem toàn bộ hình ảnh. Tuy nhiên, quy cách yêu cầu các phương thức triển khai phải phát hiện trường hợp này và cho phép người dùng tự do cuộn trong hình ảnh đó, chỉ bắt dính ở các cạnh của hình ảnh.

Ví dụ: Trang sản phẩm có hành trình

Một trường hợp phổ biến khác có thể hưởng lợi từ tính năng cố định vị trí cuộn là các trang có nhiều phần logic để cuộn theo chiều dọc, chẳng hạn như trang sản phẩm thông thường. scroll-snap-type: y proximity; phù hợp hơn cho các trường hợp như thế này. Thuộc tính này không can thiệp khi người dùng cuộn đến giữa một phần cụ thể, nhưng cũng sẽ điều chỉnh và thu hút sự chú ý đến một phần mới khi họ cuộn đến đủ gần.

Sau đây là cách thực hiện:

article {
  scroll-snap-type: y proximity;
  /* Reserve space for header plus some extra space for sneak peeking. */
  scroll-padding-top: 15vh;
  overflow-y: scroll;
}
section {
  /* Snap align start. */
  scroll-snap-align: start;
}
header {
  position: fixed;
  height: 10vh;
}
<article>
  <header> Header </header>
  <section> Section One </section>
  <section> Section Two </section>
  <section> Section Three </section>
</article>

Khoảng đệm và lề cuộn

Trang sản phẩm có tiêu đề trên cùng ở vị trí cố định. Thiết kế này cũng yêu cầu một số phần trên cùng vẫn hiển thị khi vùng chứa cuộn được đính vào để cung cấp gợi ý thiết kế cho người dùng về nội dung ở trên.

Thuộc tính scroll-padding là một thuộc tính CSS mới có thể dùng để điều chỉnh vùng có thể xem hiệu quả của vùng chứa cuộn hoặc snapport, được dùng khi tính toán các căn chỉnh cố định vị trí cuộn. Thuộc tính này xác định một phần lồng ghép so với hộp khoảng đệm của vùng chứa cuộn. Trong ví dụ của chúng tôi, phần lề bổ sung 15vh đã được thêm vào phía trên, hướng dẫn trình duyệt xem xét vị trí thấp hơn, 15vh bên dưới cạnh trên của vùng chứa cuộn, làm cạnh bắt đầu dọc để cố định vị trí cuộn. Khi điều chỉnh nhanh, cạnh bắt đầu của phần tử mục tiêu điều chỉnh nhanh sẽ trở nên bằng phẳng với vị trí mới này, do đó để lại khoảng trống ở trên.

Thuộc tính scroll-margin xác định mức độ bắt đầu được dùng để điều chỉnh hộp hiệu quả của mục tiêu đính tương tự như cách scroll-padding hoạt động trên vùng chứa cuộn đính.

Bạn có thể nhận thấy rằng hai thuộc tính này không có từ "snap". Đây là hành vi có chủ đích vì các thuộc tính này thực sự sửa đổi hộp cho tất cả các thao tác cuộn có liên quan chứ không chỉ cố định vị trí cuộn. Ví dụ: Chrome sẽ tính đến các thanh cuộn này khi tính toán kích thước trang cho các thao tác cuộn trang như PageDown và PageUp, đồng thời khi tính toán lượng cuộn cho thao tác Element.scrollIntoView().

Tương tác với các API cuộn khác

API cuộn DOM

Tính năng cố định vị trí cuộn sẽ hoạt động sau khi tất cả các thao tác cuộn (bao gồm cả những thao tác do tập lệnh bắt đầu) hoàn tất. Khi bạn sử dụng các API như Element.scrollTo, trình duyệt sẽ tính toán vị trí cuộn dự kiến của thao tác, sau đó áp dụng logic bám dính thích hợp để tìm vị trí bám dính cuối cùng. Do đó, tập lệnh người dùng không cần thực hiện bất kỳ phép tính thủ công nào để điều chỉnh.

Cuộn mượt mà

Tính năng cuộn mượt mà kiểm soát hành vi của thao tác cuộn có lập trình, còn tính năng cố định vị trí cuộn xác định đích đến của thao tác đó. Vì kiểm soát các khía cạnh trực giao của thao tác cuộn, nên bạn có thể sử dụng chúng cùng nhau và bổ sung cho nhau.

Hành vi cuộn quá mức

API hành vi cuộn quá mức kiểm soát cách cuộn được liên kết trên nhiều phần tử và không bị ảnh hưởng bởi tính năng cố định vị trí cuộn.

Lưu ý và các phương pháp hay nhất

Tránh sử dụng tính năng bắt buộc khi các phần tử mục tiêu cách xa nhau. Điều này có thể khiến nội dung giữa các vị trí chụp nhanh không thể truy cập được.

Trong nhiều trường hợp, bạn có thể thêm tính năng cố định vị trí cuộn dưới dạng một tính năng nâng cao mà không cần phát hiện tính năng. Nếu cần, hãy sử dụng @supports hoặc CSS.supports để phát hiện tính năng hỗ trợ cho CSS Scroll Snap. Tránh sử dụng scroll-snap-type, thuộc tính này cũng có trong quy cách không dùng nữa.

Phát hiện tính năng trong CSS

@supports (scroll-snap-align: start) {
  article {
    scroll-snap-type: y proximity;
    scroll-padding-top: 15vh;
    overflow-y: scroll;
  }
}

Phát hiện tính năng trong JavaScript

if (CSS.supports('scroll-snap-align: start')) {
  // use css scroll snap
} else {
  // use fallback
}

Đừng giả định rằng các API cuộn có lập trình như Element.scrollTo luôn kết thúc ở độ lệch cuộn được yêu cầu. Tính năng cố định vị trí cuộn có thể điều chỉnh độ lệch cuộn sau khi quá trình cuộn có lập trình hoàn tất. Xin lưu ý rằng đây không phải là một giả định phù hợp ngay cả trước khi cố định vị trí cuộn, vì thao tác cuộn có thể đã bị gián đoạn vì những lý do khác, nhưng điều này đặc biệt đúng với tính năng cố định vị trí cuộn.

Công việc trong tương lai

Trải nghiệm cuộn là trọng tâm của một cuộc khảo sát gần đây của nhóm Chrome. Kết quả khảo sát cho thấy một số khía cạnh cần cải thiện thêm để thu hẹp khoảng cách giữa thư viện trình bổ trợ và CSS. Công việc sắp tới sẽ tập trung vào scroll-snap, bao gồm:

  1. Phạm vi cung cấp và khả năng tương thích của API trên các trình duyệt.
  2. Làm việc với các API CSS mới như scroll-start.
  3. Xử lý các sự kiện JS mới như snapChanged().