Kiểm soát thao tác cuộn – tuỳ chỉnh các hiệu ứng kéo để làm mới và mục bổ sung

TL;DR

Thuộc tính CSS overscroll-behavior cho phép nhà phát triển ghi đè hành vi cuộn tràn mặc định của trình duyệt khi đạt đến đầu/cuối nội dung. Các trường hợp sử dụng bao gồm tắt tính năng kéo để làm mới trên thiết bị di động, xoá hiệu ứng phát sáng cuộn quá mức và hiệu ứng dải cao su, đồng thời ngăn nội dung trang cuộn khi ở dưới một phương thức/lớp phủ.

Thông tin khái quát

Ranh giới cuộn và chuỗi cuộn

Tạo chuỗi trên Chrome Android.

Cuộn là một trong những cách cơ bản nhất để tương tác với một trang, nhưng một số mẫu trải nghiệm người dùng nhất định có thể khó xử lý do các hành vi mặc định kỳ quặc của trình duyệt. Ví dụ: hãy lấy một ngăn ứng dụng có số lượng lớn mục mà người dùng có thể phải cuộn qua. Khi chúng cuộn xuống dưới cùng, vùng chứa mục bổ sung sẽ ngừng cuộn vì không còn nội dung nào để sử dụng. Nói cách khác, người dùng đạt đến "ranh giới cuộn". Tuy nhiên, hãy lưu ý điều gì sẽ xảy ra nếu người dùng tiếp tục cuộn. Nội dung ở phía sau ngăn bắt đầu cuộn! Vùng chứa mẹ sẽ tiếp quản tính năng cuộn; chính trang chính trong ví dụ.

Hóa ra hành vi này được gọi là chuỗi cuộn; hành vi mặc định của trình duyệt khi cuộn nội dung. Thông thường, giá trị mặc định khá tốt, nhưng đôi khi không mong muốn hoặc thậm chí là không mong đợi. Một số ứng dụng có thể muốn cung cấp trải nghiệm người dùng khác khi người dùng chạm vào ranh giới cuộn.

Hiệu ứng kéo để làm mới

Kéo để làm mới là một cử chỉ trực quan được các ứng dụng di động như Facebook và Twitter phổ biến. Việc kéo xuống nguồn cấp dữ liệu mạng xã hội và phát hành sẽ tạo không gian mới để tải các bài đăng gần đây hơn. Trên thực tế, trải nghiệm người dùng đặc biệt này đã trở nên rất phổ biến đến nỗi các trình duyệt di động như Chrome trên Android cũng đã áp dụng hiệu ứng tương tự. Thao tác vuốt xuống ở đầu trang sẽ làm mới toàn bộ trang:

Tính năng kéo để làm mới
tuỳ chỉnh của Twitter khi làm mới nguồn cấp dữ liệu trong PWA.
Thao tác kéo để làm mới gốc của Chrome Android
làm mới toàn bộ trang.

Đối với các trường hợp như PWA của Twitter, bạn nên tắt thao tác kéo để làm mới gốc. Tại sao? Trong ứng dụng này, có thể bạn không muốn người dùng vô tình làm mới trang. Bạn cũng có thể thấy ảnh động làm mới hai lần! Ngoài ra, bạn nên tuỳ chỉnh hành động của trình duyệt để phù hợp hơn với thương hiệu của trang web. Đáng tiếc là loại tuỳ chỉnh này khó thực hiện. Các nhà phát triển sẽ viết JavaScript không cần thiết, thêm trình nghe cảm ứng không thụ động (chặn cuộn) hoặc gắn toàn bộ trang trong một <div> 100vw/vh (để ngăn trang bị tràn). Các giải pháp này có tác động tiêu cực được ghi nhận rõ ràng đến hiệu suất cuộn.

Chúng tôi có thể làm tốt hơn!

Xin giới thiệu overscroll-behavior

Thuộc tính overscroll-behavior là một tính năng CSS mới giúp kiểm soát hành vi của những gì xảy ra khi bạn cuộn quá mức một vùng chứa (bao gồm cả chính trang). Bạn có thể sử dụng thuộc tính này để huỷ chuỗi cuộn, tắt/tuỳ chỉnh thao tác kéo để làm mới, tắt hiệu ứng đàn hồi trên iOS (khi Safari triển khai overscroll-behavior) và nhiều thao tác khác. Điều hay nhất là việc sử dụng overscroll-behavior không ảnh hưởng xấu đến hiệu suất trang như các thủ thuật được đề cập trong phần giới thiệu!

Thuộc tính này có thể nhận 3 giá trị:

  1. tự động – Mặc định. Các thao tác cuộn bắt nguồn từ phần tử này có thể truyền đến các phần tử cấp trên.
  2. contain (chứa) – ngăn chặn việc tạo chuỗi cuộn. Các thao tác cuộn không truyền đến các thành phần cấp trên nhưng các hiệu ứng cục bộ trong nút sẽ hiển thị. Ví dụ: hiệu ứng sáng khi cuộn quá mức trên Android hoặc hiệu ứng đàn hồi trên iOS sẽ thông báo cho người dùng khi họ chạm đến ranh giới cuộn. Lưu ý: việc sử dụng overscroll-behavior: contain trên phần tử html sẽ ngăn các thao tác điều hướng cuộn quá mức.
  3. none – giống như contain nhưng cũng ngăn chặn các hiệu ứng cuộn hết trong chính nút (ví dụ: hiệu ứng cuộn hết trên Android hoặc hiệu ứng cao su trên iOS).

Hãy cùng tìm hiểu một số ví dụ để biết cách sử dụng overscroll-behavior.

Ngăn cuộn thoát khỏi phần tử vị trí cố định

Tình huống hộp trò chuyện

Nội dung bên dưới cửa sổ trò chuyện cũng cuộn :(

Hãy cân nhắc đặt hộp trò chuyện cố định ở cuối trang. Ý định là hộp trò chuyện là một thành phần độc lập và cuộn riêng biệt với nội dung phía sau. Tuy nhiên, do tính năng tạo chuỗi cuộn, tài liệu sẽ bắt đầu cuộn ngay khi người dùng nhấn vào tin nhắn cuối cùng trong nhật ký trò chuyện.

Đối với ứng dụng này, bạn nên để các cuộn bắt nguồn từ hộp trò chuyện vẫn nằm trong cuộc trò chuyện. Chúng ta có thể thực hiện việc đó bằng cách thêm overscroll-behavior: contain vào phần tử chứa tin nhắn trò chuyện:

#chat .msgs {
  overflow: auto;
  overscroll-behavior: contain;
  height: 300px;
}

Về cơ bản, chúng ta đang tạo một sự phân tách hợp lý giữa ngữ cảnh cuộn của hộp trò chuyện và trang chính. Kết quả cuối cùng là trang chính vẫn ở lại khi người dùng chuyển đến đầu/cuối nhật ký trò chuyện. Các thao tác cuộn bắt đầu trong hộp trò chuyện không được truyền ra ngoài.

Trường hợp lớp phủ trang

Một biến thể khác của trường hợp "cuộn xuống dưới" là khi bạn thấy nội dung cuộn phía sau một lớp phủ vị trí cố định. Bạn cần có một overscroll-behavior để tặng quà! Trình duyệt đang cố gắng giúp ích nhưng cuối cùng lại khiến trang web trông có lỗi.

Ví dụ – cửa sổ bật lên có và không có overscroll-behavior: contain:

Trước: nội dung trang cuộn bên dưới lớp phủ.
Sau khi: nội dung trang không cuộn bên dưới lớp phủ.

Tắt tính năng kéo để làm mới

Tắt thao tác kéo để làm mới chỉ bằng một dòng CSS. Chỉ cần ngăn chặn việc tạo chuỗi cuộn trên toàn bộ phần tử xác định khung nhìn. Trong hầu hết các trường hợp, đó là <html> hoặc <body>:

body {
  /* Disables pull-to-refresh but allows overscroll glow effects. */
  overscroll-behavior-y: contain;
}

Với việc bổ sung đơn giản này, chúng ta sẽ khắc phục ảnh động kéo để làm mới hai lần trong bản minh hoạ hộp trò chuyện và có thể triển khai hiệu ứng tuỳ chỉnh sử dụng ảnh động tải gọn gàng hơn. Toàn bộ hộp thư đến cũng bị làm mờ khi hộp thư đến làm mới:

Trước
Sau

Dưới đây là một đoạn của mã đầy đủ:

<style>
  body.refreshing #inbox {
    filter: blur(1px);
    touch-action: none; /* prevent scrolling */
  }
  body.refreshing .refresher {
    transform: translate3d(0,150%,0) scale(1);
    z-index: 1;
  }
  .refresher {
    --refresh-width: 55px;
    pointer-events: none;
    width: var(--refresh-width);
    height: var(--refresh-width);
    border-radius: 50%;
    position: absolute;
    transition: all 300ms cubic-bezier(0,0,0.2,1);
    will-change: transform, opacity;
    ...
  }
</style>

<div class="refresher">
  <div class="loading-bar"></div>
  <div class="loading-bar"></div>
  <div class="loading-bar"></div>
  <div class="loading-bar"></div>
</div>

<section id="inbox"><!-- msgs --></section>

<script>
  let _startY;
  const inbox = document.querySelector('#inbox');

  inbox.addEventListener('touchstart', e => {
    _startY = e.touches[0].pageY;
  }, {passive: true});

  inbox.addEventListener('touchmove', e => {
    const y = e.touches[0].pageY;
    // Activate custom pull-to-refresh effects when at the top of the container
    // and user is scrolling up.
    if (document.scrollingElement.scrollTop === 0 && y > _startY &&
        !document.body.classList.contains('refreshing')) {
      // refresh inbox.
    }
  }, {passive: true});
</script>

Tắt hiệu ứng ánh sáng khi cuộn quá mức và hiệu ứng cao su

Để tắt hiệu ứng nảy khi đạt đến ranh giới cuộn, hãy sử dụng overscroll-behavior-y: none:

body {
  /* Disables pull-to-refresh and overscroll glow effect.
     Still keeps swipe navigations. */
  overscroll-behavior-y: none;
}
Trước: khi chạm vào ranh giới cuộn, một ánh sáng sẽ xuất hiện.
Sau: chế độ phát sáng bị tắt.

Bản minh hoạ đầy đủ

Khi kết hợp tất cả, bản minh hoạ hộp trò chuyện đầy đủ sẽ sử dụng overscroll-behavior để tạo ảnh động kéo để làm mới tuỳ chỉnh và tắt tính năng cuộn để thoát khỏi tiện ích hộp trò chuyện. Điều này mang lại trải nghiệm người dùng tối ưu mà bạn khó có thể đạt được nếu không có CSS overscroll-behavior.

Xem bản minh hoạ | Nguồn