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 overscroll-behavior CSS 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 việc tắt tính năng kéo để làm mới trên thiết bị di động, xoá hiệu ứng ánh sáng khi cuộn quá mức và hiệu ứng đàn hồi, đồng thời ngăn nội dung trang cuộn khi nằm bên dưới một cửa sổ bật lên/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ụ: lấy một ngăn ứng dụng có nhiều các 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 tràn 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! Thao tác cuộn là bị chiếm đoạt bởi vùng chứa mẹ; trang chính trong ví dụ.

Hoá ra hành vi này được gọi là chuỗi cuộn; giá trị mặc định của trình duyệt khi cuộn nội dung. Thông thường, chế độ mặc định khá tốt, nhưng đôi khi không như mong muốn hoặc thậm chí là ngoài dự kiến. 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. Thu hút sự chú ý trên trang mạng xã hội và phát hành các video tạo mới khoảng trống để tải thêm các bài đăng gần đây. Trên thực tế, trải nghiệm người dùng cụ thể này đã trở nên rất phổ biến đến mức các trình duyệt di động như Chrome trên Android cũng đã áp dụng hiệu ứng tương tự. Việ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
sẽ làm mới toàn bộ .

Đối với những tình huống như PWA của 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, 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 có thể tuỳ chỉnh thao tác của trình duyệt để phù hợp hơn với thương hiệu của trang web. Thật không may là kiểu tuỳ chỉnh này đã bị khó kéo ra. Nhà phát triển cuối cùng viết những JavaScript không cần thiết, hãy thêm không thụ động trình nghe cảm ứng (chặn cuộn) hoặc dán toàn bộ trang ở dạng 100vw/vh <div> (để 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, chẳng hạn như mẹo hay đượ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 ngăn tình trạng cuộn quá mức thao tác điều hướng.
  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ụ để xem 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ạo 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 cuộc trò chuyện nhật ký.

Đối với ứng dụng này, sẽ phù hợp hơn nếu bạn có cuộn bắt nguồn từ hộp trò chuyện vẫn ở 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 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 ở nguyên vị trí khi người dùng đế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 hữu ích nhưng nó sẽ khiến trang web trông có vẻ lỗi.

Ví dụ – cửa sổ phụ 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 là một dòng CSS duy nhất. Chỉ ngăn chặn 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 thao tác bổ sung đơn giản này, chúng ta sửa hai ảnh động kéo để làm mới trong bản minh hoạ hộp trò chuyện và có thể thay vào đó, hãy 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

Sau đây là một đ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 ánh sáng khi cuộn quá mức và hiệu ứng đàn hồi

Để tắt hiệu ứng bật lại khi chạm vào 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 hiệu quả tối ưu trải nghiệm người dùng khó có thể đạt được nếu không có CSS overscroll-behavior.

Xem bản minh hoạ | Nguồn