Tạo thành phần cuộn nội dung đa phương tiện

Tổng quan cơ bản về cách tạo một thành phần scrollview ngang thích ứng cho TV, điện thoại, máy tính, v.v.

Trong bài đăng này, tôi muốn chia sẻ suy nghĩ về cách tạo trải nghiệm cuộn ngang cho web sao cho tối giản, thích ứng, dễ truy cập và hoạt động trên nhiều trình duyệt và nền tảng (chẳng hạn như TV!). Dùng thử bản minh hoạ.

Bản minh hoạ

Nếu bạn thích xem video, thì đây là phiên bản video của bài đăng này trên YouTube:

Tổng quan

Chúng ta sẽ tạo một bố cục cuộn ngang để lưu trữ hình thu nhỏ của nội dung nghe nhìn hoặc sản phẩm. Thành phần này bắt đầu dưới dạng một danh sách <ul> đơn giản nhưng được chuyển đổi bằng CSS thành một trải nghiệm cuộn mượt mà và thoả mãn, thể hiện hình ảnh và gắn chúng vào một lưới. JavaScript được thêm vào để hỗ trợ các hoạt động tương tác bằng chỉ mục di động, giúp người dùng bàn phím bỏ qua việc di chuyển qua hơn 100 mục. Ngoài ra, một truy vấn nội dung nghe nhìn thử nghiệm (prefers-reduced-data) được dùng để chuyển trình cuộn nội dung nghe nhìn thành một trình cuộn tiêu đề có dung lượng nhẹ.

Bắt đầu bằng mã đánh dấu hỗ trợ tiếp cận

Thanh cuộn nội dung nghe nhìn chỉ bao gồm một vài thành phần cốt lõi, đó là danh sách có các mục. Danh sách, ở dạng đơn giản nhất, có thể đi khắp thế giới và được mọi người sử dụng một cách rõ ràng. Người dùng truy cập vào trang này có thể duyệt xem danh sách và nhấp vào một đường liên kết để xem một mặt hàng. Đây là cơ sở hỗ trợ tiếp cận của chúng tôi.

Phân phối danh sách có phần tử <ul>:

<ul class="horizontal-media-scroller">
  <li></li>
  <li></li>
  <li></li>
  ...
<ul>

Tạo các mục trong danh sách có thể tương tác bằng phần tử <a>:

<li>
  <a href="#">
    ...
  </a>
</li>

Sử dụng phần tử <figure> để biểu thị một cách có ý nghĩa ngữ nghĩa cho hình ảnh và chú thích của hình ảnh đó:

<figure>
  <picture>
    <img alt="..." loading="lazy" src="https://picsum.photos/500/500?1">
  </picture>
  <figcaption>Legends</figcaption>
</figure>

Lưu ý các thuộc tính altloading trên <img>. Văn bản thay thế cho một trình cuộn nội dung nghe nhìn là một cơ hội cải thiện trải nghiệm người dùng để giúp mang lại thêm bối cảnh cho hình thu nhỏ hoặc làm văn bản dự phòng nếu hình ảnh không tải được, hoặc cung cấp giao diện người dùng bằng giọng nói cho những người dùng dựa vào công nghệ hỗ trợ như trình đọc màn hình. Tìm hiểu thêm qua bài viết 5 quy tắc vàng để viết văn bản thay thế tuân thủ.

Thuộc tính loading chấp nhận từ khoá lazy như một cách để báo hiệu rằng nguồn hình ảnh này chỉ được tìm nạp khi hình ảnh nằm trong khung hiển thị. Điều này có thể rất hữu ích đối với các danh sách lớn, vì người dùng sẽ chỉ tải hình ảnh cho những mục mà họ đã cuộn để xem.

Hỗ trợ lựa chọn ưu tiên của người dùng về bảng phối màu

Sử dụng color-scheme làm thẻ <meta> để báo hiệu cho trình duyệt rằng trang của bạn muốn cả kiểu user-agent sáng và tối được cung cấp. Đây là một chế độ tối hoặc chế độ sáng miễn phí, tuỳ thuộc vào cách bạn nhìn nhận:

<meta name="color-scheme" content="dark light">

Thẻ meta cung cấp tín hiệu sớm nhất có thể, vì vậy trình duyệt có thể chọn màu nền tối mặc định nếu người dùng có lựa chọn ưu tiên về giao diện tối. Điều này có nghĩa là các thao tác di chuyển giữa các trang của trang web sẽ không nhấp nháy nền canvas màu trắng giữa các lần tải. Giao diện tối liền mạch giữa các lần tải, dễ chịu hơn nhiều cho mắt.

Tìm hiểu thêm từ Thomas Steiner tại https://web.dev/color-scheme/.

Thêm nội dung

Với cấu trúc nội dung ul > li > a > figure > picture > img nêu trên, nhiệm vụ tiếp theo là thêm hình ảnh và tiêu đề để cuộn qua. Tôi đã đóng gói bản minh hoạ bằng văn bản và hình ảnh giữ chỗ tĩnh, nhưng bạn có thể thoải mái sử dụng nguồn dữ liệu yêu thích của mình.

Thêm kiểu bằng CSS

Giờ là lúc CSS lấy danh sách nội dung chung này và biến nó thành một trải nghiệm. Netflix, các cửa hàng ứng dụng và nhiều trang web cũng như ứng dụng khác sử dụng các vùng cuộn ngang để đóng gói khung hiển thị bằng các danh mục và lựa chọn.

Tạo bố cục trình cuộn

Bạn cần tránh cắt nội dung trong bố cục hoặc dựa vào việc cắt bớt văn bản bằng dấu ba chấm. Nhiều bộ truyền hình có thanh cuộn nội dung giống như thanh cuộn này, nhưng thường xuyên sử dụng dấu ba chấm để cắt bớt nội dung. Bố cục này không có! Tính năng này cũng cho phép nội dung nghe nhìn ghi đè kích thước cột, giúp 1 bố cục đủ linh hoạt để xử lý nhiều tổ hợp thú vị.

2 hàng cuộn được hiển thị. Một trong số đó không có dấu ba chấm, tức là cao hơn và mỗi tiêu đề đều dễ đọc. Một chế độ khác ngắn hơn và nhiều tiêu đề bị cắt bớt bằng dấu ba chấm.

Vùng chứa cho phép ghi đè kích thước cột bằng cách cung cấp kích thước mặc định dưới dạng một thuộc tính tuỳ chỉnh. Bố cục lưới này có ý kiến riêng về kích thước cột, chỉ quản lý khoảng cách và hướng:

.horizontal-media-scroller {
  --size: 150px;

  display: grid;
  grid-auto-flow: column;
  gap: calc(var(--gap) / 2); /* parent owned value for children to be relative to*/
  margin: 0;
}

Sau đó, phần tử <picture> sẽ dùng thuộc tính tuỳ chỉnh này để tạo tỷ lệ khung hình cơ bản của chúng ta: một hộp:

.horizontal-media-scroller {
  --size: 150px;

  display: grid;
  grid-auto-flow: column;
  gap: calc(var(--gap) / 2);
  margin: 0;

  & picture {
    inline-size: var(--size);
    block-size: var(--size);
  }
}

Chỉ cần thêm một vài kiểu nhỏ nữa, bạn sẽ hoàn thành phần cơ bản của trình cuộn nội dung nghe nhìn:

.horizontal-media-scroller {
  --size: 150px;

  display: grid;
  grid-auto-flow: column;
  gap: calc(var(--gap) / 2);
  margin: 0;

  overflow-x: auto;
  overscroll-behavior-inline: contain;

  & > li {
    display: inline-block; /* removes the list-item bullet */
  }

  & picture {
    inline-size: var(--size);
    block-size: var(--size);
  }
}

Chế độ cài đặt overflow sẽ thiết lập <ul> để cho phép di chuyển và điều hướng bằng bàn phím thông qua danh sách của nó, sau đó mỗi phần tử con trực tiếp <li> sẽ bị xoá ::marker bằng cách nhận một loại hiển thị mới là inline-block.

Tuy nhiên, các hình ảnh này chưa thích ứng và xuất hiện ngay bên ngoài các hộp chứa chúng. Hãy điều chỉnh chúng bằng một số kích thước, chế độ vừa khít và kiểu đường viền, cũng như một hiệu ứng chuyển màu nền khi chúng đang tải lười:

img {
  /* smash into whatever box it's in */
  inline-size: 100%;
  block-size: 100%;

  /* don't squish but do cover the space */
  object-fit: cover;

  /* soften the edges */
  border-radius: 1ex;
  overflow: hidden;

  /* if empty, show a gradient placeholder */
  background-image:
    linear-gradient(
      to bottom,
      hsl(0 0% 40%),
      hsl(0 0% 20%)
    );
}

Khoảng đệm cuộn

Việc căn chỉnh với nội dung trang, cộng với diện tích bề mặt cuộn tràn viền là yếu tố quan trọng để tạo ra một thành phần hài hoà và tối giản.

Để hoàn thành bố cục cuộn tràn viền phù hợp với kiểu chữ và các đường bố cục, hãy sử dụng padding khớp với scroll-padding:

.horizontal-media-scroller {
  --size: 150px;

  display: grid;
  grid-auto-flow: column;
  gap: calc(var(--gap) / 2);
  margin: 0;

  overflow-x: auto;
  overscroll-behavior-inline: contain;

  padding-inline: var(--gap);
  scroll-padding-inline: var(--gap);
  padding-block: calc(var(--gap) / 2); /* make space for scrollbar and focus outline */
}

Khắc phục lỗi khoảng đệm cuộn ngang Ở trên cho thấy việc thêm khoảng đệm vào một vùng chứa có thể cuộn dễ dàng như thế nào, nhưng vẫn còn các vấn đề về khả năng tương thích với vùng chứa đó (đã được khắc phục trong Chromium 91 trở lên!). Xem tại đây để biết một chút về lịch sử, nhưng phiên bản ngắn là phần đệm không phải lúc nào cũng được tính đến trong khung hiển thị có thể cuộn.

Một hộp được làm nổi bật ở phía cuối hàng của mục danh sách cuối cùng, cho thấy khoảng đệm và phần tử có cùng chiều rộng để tạo sự căn chỉnh mong muốn.

Để đánh lừa trình duyệt đưa khoảng đệm vào cuối thanh cuộn, tôi sẽ nhắm đến số liệu cuối cùng trong mỗi danh sách và thêm một phần tử giả là lượng khoảng đệm mong muốn.

.horizontal-media-scroller > li:last-of-type figure {
  position: relative;

  &::after {
    content: "";
    position: absolute;

    inline-size: var(--gap);
    block-size: 100%;

    inset-block-start: 0;
    inset-inline-end: calc(var(--gap) * -1);
  }
}

Việc sử dụng các thuộc tính logic cho phép trình cuộn nội dung nghe nhìn hoạt động ở mọi chế độ viết và hướng tài liệu.

cố định vị trí cuộn

Vùng chứa có thể cuộn có nội dung tràn có thể trở thành một khung hiển thị có tính năng căn chỉnh nhanh chỉ bằng một dòng CSS. Sau đó, các phần tử con sẽ chỉ định cách chúng muốn căn chỉnh với khung hiển thị đó.

.horizontal-media-scroller {
  --size: 150px;

  display: grid;
  grid-auto-flow: column;
  gap: calc(var(--gap) / 2);
  margin: 0;

  overflow-x: auto;
  overscroll-behavior-inline: contain;

  padding-inline: var(--gap);
  scroll-padding-inline: var(--gap);
  padding-block-end: calc(var(--gap) / 2);

  scroll-snap-type: inline mandatory;

  & figure {
    scroll-snap-align: start;
  }
}

Tập trung

Thành phần này được lấy cảm hứng từ mức độ phổ biến rộng rãi của thành phần này trên TV, trong Cửa hàng ứng dụng và nhiều nơi khác. Nhiều nền tảng trò chơi điện tử sử dụng một trình cuộn nội dung nghe nhìn tương tự như trình cuộn này làm bố cục chính cho màn hình chính. Tiêu điểm là một khoảnh khắc quan trọng trong trải nghiệm người dùng, chứ không chỉ là một điểm bổ sung nhỏ. Hãy tưởng tượng bạn đang sử dụng bộ cuộn nội dung nghe nhìn này trên ghế dài bằng điều khiển từ xa, hãy cải thiện một chút cho hoạt động tương tác đó:

.horizontal-media-scroller a {
  outline-offset: 12px;

  &:focus {
    outline-offset: 7px;
  }

  @media (prefers-reduced-motion: no-preference) {
    & {
      transition: outline-offset .25s ease;
    }
  }
}

Thao tác này đặt kiểu đường viền tiêu điểm 7px ra khỏi hộp, tạo cho hộp một khoảng trống vừa ý. Nếu người dùng không có lựa chọn ưu tiên nào về việc giảm chuyển động, thì độ lệch sẽ được chuyển đổi, mang lại chuyển động tinh tế cho sự kiện lấy tiêu điểm.

Chỉ mục di động

Người dùng gamepad và bàn phím cần được chú ý đặc biệt trong những danh sách dài gồm nội dung và lựa chọn có thể di chuyển này. Mẫu phổ biến để giải quyết vấn đề này được gọi là chỉ mục di động. Đây là khi một vùng chứa các mục được lấy tiêu điểm bằng bàn phím nhưng chỉ có 1 phần tử con được phép giữ tiêu điểm tại một thời điểm. Trải nghiệm này được thiết kế để cho phép bỏ qua danh sách có thể dài gồm nhiều mục, thay vì nhấn phím tab hơn 50 lần để đến cuối danh sách.

Có 300 mục trong trình cuộn đầu tiên của bản minh hoạ đó. Chúng ta có thể làm tốt hơn là bắt họ phải đi qua tất cả các phần để đến phần tiếp theo.

Để tạo trải nghiệm này, JavaScript cần theo dõi các sự kiện bàn phím và sự kiện tiêu điểm. Tôi đã tạo một thư viện nguồn mở nhỏ trên npm để giúp người dùng dễ dàng đạt được trải nghiệm này. Sau đây là cách sử dụng cho 3 thành phần cuộn:

import {rovingIndex} from 'roving-ux';

rovingIndex({
  element: someElement
});

Bản minh hoạ này truy vấn tài liệu để tìm các trình cuộn và gọi hàm rovingIndex() cho từng trình cuộn. Truyền rovingIndex() phần tử để có được trải nghiệm di chuyển, chẳng hạn như vùng chứa danh sách và bộ chọn truy vấn đích, trong trường hợp các đích lấy tiêu điểm không phải là phần tử con trực tiếp.

document.querySelectorAll('.horizontal-media-scroller')
  .forEach(scroller =>
    rovingIndex({
      element: scroller,
      target: 'a',
}))

Để tìm hiểu thêm về hiệu ứng này, hãy xem thư viện nguồn mở roving-ux.

Tỷ lệ khung hình

Tại thời điểm viết bài này, khả năng hỗ trợ aspect-ratio nằm sau một cờ trong Firefox nhưng có sẵn trong các trình duyệt Chromium hoặc hộp giải mã tín hiệu truyền hình. Vì bố cục lưới của trình cuộn nội dung nghe nhìn chỉ chỉ định hướng và khoảng cách, nên kích thước có thể thay đổi trong một truy vấn nội dung nghe nhìn. Tính năng này kiểm tra khả năng hỗ trợ tỷ lệ khung hình. Tăng cường dần dần một số trình cuộn nội dung nghe nhìn linh hoạt hơn.

Một hộp có tỷ lệ khung hình 4:4 xuất hiện bên cạnh các tỷ lệ thiết kế khác được dùng là 16:9 và 4:3

@supports (aspect-ratio: 1) {
  .horizontal-media-scroller figure > picture {
    inline-size: auto; /* for a block-size driven ratio */
    aspect-ratio: 1; /* boxes by default */

    @nest section:nth-child(2) & {
      aspect-ratio: 16/9;
    }

    @nest section:nth-child(3) & {
      /* double the size of the others */
      block-size: calc(var(--size) * 2);
      aspect-ratio: 4/3;

      /* adjust size to fit more items into the viewport */
      @media (width <= 480px) {
        block-size: calc(var(--size) * 1.5);
      }
    }
  }
}

Nếu trình duyệt hỗ trợ cú pháp aspect-ratio, thì hình ảnh trong trình cuộn nội dung nghe nhìn sẽ được nâng cấp lên kích thước aspect-ratio. Khi sử dụng cú pháp lồng bản nháp, mỗi bức ảnh sẽ thay đổi tỷ lệ khung hình tuỳ thuộc vào việc đó là hàng thứ nhất, thứ hai hay thứ ba. Cú pháp nest cũng cho phép thiết lập một số điều chỉnh nhỏ về khung hiển thị, ngay tại đó cùng với logic định cỡ khác.

Với CSS đó, khi tính năng này có trong nhiều công cụ trình duyệt hơn, một bố cục dễ quản lý nhưng bắt mắt hơn sẽ hiển thị.

Ưu tiên giảm dữ liệu

Mặc dù kỹ thuật tiếp theo này chỉ có sau một cờ trong Canary, tôi muốn chia sẻ cách tôi có thể tiết kiệm đáng kể thời gian tải trang và mức sử dụng dữ liệu chỉ bằng một vài dòng CSS. Truy vấn nội dung nghe nhìn prefers-reduced-data từ cấp 5 cho phép hỏi xem thiết bị có ở trạng thái giảm dữ liệu nào hay không, chẳng hạn như chế độ trình tiết kiệm dữ liệu. Nếu có, tôi có thể sửa đổi tài liệu và trong trường hợp này, tôi có thể ẩn hình ảnh.

ALT_TEXT_HERE

figure {
  @media (prefers-reduced-data: reduce) {
    & {
      min-inline-size: var(--size);

      & > picture {
        display: none;
      }
    }
  }
}

Người dùng vẫn có thể điều hướng nội dung nhưng không phải trả phí tải hình ảnh có kích thước lớn. Đây là trang web trước khi thêm CSS prefers-reduced-data:

(7 yêu cầu, 100 kb tài nguyên trong 131 mili giây)

ALT_TEXT_HERE

Sau đây là hiệu suất của trang web sau khi thêm CSS prefers-reduced-data:

ALT_TEXT_HERE

(71 yêu cầu, 1,2 MB tài nguyên trong 1,07 giây)

Giảm 64 yêu cầu, tức là khoảng 60 hình ảnh trong khung hiển thị (các thử nghiệm được thực hiện trên màn hình rộng) của thẻ trình duyệt này, tăng tốc độ tải trang lên khoảng 80% và 10% dữ liệu trên mạng. CSS khá mạnh mẽ.

Kết luận

Giờ bạn đã biết cách tôi làm, vậy bạn sẽ làm như thế nào?! 🙂

Hãy đa dạng hoá các phương pháp và tìm hiểu tất cả các cách để xây dựng trên web. Tạo một bản minh hoạ trên Codepen hoặc tự lưu trữ bản minh hoạ của riêng bạn, sau đó gửi cho tôi qua Twitter. Tôi sẽ thêm bản minh hoạ đó vào phần Bản phối lại của cộng đồng bên dưới.

Nguồn

Bản phối lại của cộng đồng

Chưa có nội dung nào ở đây!