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

Eric Bidelman
Majid Valipour

TL;DR

Thuộc tính CSS overscroll-behavior cho phép nhà phát triển ghi đè hành vi cuộn mục bổ sung mặc định của trình duyệt khi di chuyển lên đầu/dưới cùng của 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 dải cao su và ánh sáng cuộn quá mức, đồng thời ngăn nội dung trang cuộn khi ở bên dưới một phương thức/lớp phủ.

Thông tin khái quát

Cuộn ranh giới và tạo chuỗi cuộn

Chuỗi cuộn 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 bạn có thể gặp khó khăn khi xử lý một số mẫu trải nghiệm người dùng nhất định do các hành vi mặc định kỳ quặc của trình duyệt. Ví dụ: lấy một ngăn ứng dụng có số lượng lớn các mục mà người dùng có thể phải cuộn qua. Khi 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 để 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 chú ý đến đ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! Thao tác cuộn sẽ bị vùng chứa mẹ đảm nhận; trong ví dụ này chính là trang chính.

Hoá ra hành vi này được gọi là tạo 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á là đẹp, nhưng đôi khi nó không như mong muốn hoặc thậm chí không như 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 đạt đến ranh giới cuộn.

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

Kéo để làm mới là một thao tác trực quan phổ biến trên các ứng dụng dành cho thiết bị di động, chẳng hạn như Facebook và Twitter. Việc kéo xuống một nguồn cấp dữ liệu mạng xã hội và phát hành sẽ tạo ra 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ụ thể này đã trở nên phổ biến đến mức các trình duyệt dành cho thiết bị di động như Chrome trên Android cũng áp dụng hiệu ứng tương tự. Vuốt xuống ở đầu trang sẽ làm mới toàn bộ trang:

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 của họ.
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 tình huống như PWA Twitter, bạn có thể tắt thao tác kéo để làm mới gốc. Tại sao? Trong ứng dụng này, bạn có thể 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, điều chỉnh hành động cho phù hợp hơn với thương hiệu của trang web. Điều đáng tiếc là loại tuỳ chỉnh này rất khó thực hiện. Các nhà phát triển phải 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 cố định toàn bộ trang ở dạng <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 đầy đủ đối với hiệu suất cuộn.

Chúng tôi có thể cải thiện!

Xin giới thiệu overscroll-behavior

Thuộc tính overscroll-behavior là một tính năng CSS mới kiểm soát hành vi của điều sẽ 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 tính năng này để huỷ chuỗi cuộn, tắt/tuỳ chỉnh thao tác kéo để làm mới, tắt các hiệu ứng dải cao su trên iOS (khi Safari triển khai overscroll-behavior) và làm nhiều việc khác. Điều tuyệt vời 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 mẹo vặt đã đề cập trong phần giới thiệu!

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

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

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

Ngăn không cho thao tác cuộn thoát khỏi một 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 được :(

Hãy cân nhắc đặt một hộp trò chuyện cố định ở cuối trang. Mục đích là hộp trò chuyện là một thành phần độc lập và sẽ cuộn riêng biệt với nội dung phía sau. Tuy nhiên, do chuỗi cuộn, tài liệu 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 để nội dung cuộn bắt nguồn từ hộp trò chuyện nằm trong cuộc trò chuyện. Chúng ta có thể thực hiện việc này 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 tôi đang tạo một sự phân tách logic giữa ngữ cảnh cuộn của hộp trò chuyện và trang chính. Kết quả là trang chính vẫn hiển thị khi người dùng chuyển đến đầu/dưới cùng của nhật ký trò chuyện. Các thao tác cuộn bắt đầu trong hộp trò chuyện không truyền ra.

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

Một biến thể khác của trường hợp "cuộn dưới" là khi bạn thấy nội dung cuộn phía sau lớp phủ vị trí cố định. Một phần quà đã hết tồn tại overscroll-behavior đã sẵn sàng! Trình duyệt đang cố gắng trở nên hữu ích nhưng lại khiến trang web trông có vẻ bị lỗi.

Ví dụ – phương thức có và không có overscroll-behavior: contain:

Trước: nội dung trang cuộn xuống bên dưới lớp phủ.
Sau: 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 là một dòng CSS. Bạn chỉ cần ngăn việc cuộn chuỗi 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 thao tác bổ sung đơn giản này, chúng tôi sửa các ảnh động kéo để làm mới kép trong bản minh hoạ hộp trò chuyện và thay vào đó có thể triển khai một 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ị mờ khi hộp thư đến được làm mới:

Trước
Sau

Dưới đây là đoạn mã 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 dải cao su và toả sáng quá mức

Để tắt hiệu ứng thoát 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 đạt đến ranh giới cuộn, bạn sẽ thấy một ánh sáng.
Sau: tính năng toả sáng bị tắt.

Bản minh hoạ đầy đủ

Kết hợp lại với nhau, bản minh hoạ hộp trò chuyện đầy đủ sử dụng overscroll-behavior để tạo ảnh động kéo để làm mới tuỳ chỉnh và ngăn các thao tác 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à khó có thể đạt được nếu không có CSS overscroll-behavior.

Xem bản minh hoạ | Nguồn