Xây dựng thành phần của trình cuộn nội dung nghe nhìn

Tổng quan cơ bản về cách tạo thành phần hiển thị cuộn 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ác cách tạo trải nghiệm cuộn ngang cho web một cách tối giản, thích ứng, dễ tiếp cận và hoạt động trên các trình duyệt và nền tảng (như TV!). Hãy dùng thử bản minh hoạ.

Bản minh hoạ

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

Tổng quan

Chúng ta sẽ xây dựng 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 là một danh sách <ul> khiêm tốn nhưng được chuyển đổi bằng CSS thành trải nghiệm cuộn mượt mà và hài lòng, hiển thị hình ảnh và chụp nhanh các hình ảnh đó vào lưới. JavaScript được thêm vào để hỗ trợ các hoạt động tương tác với chỉ mục di chuyển, 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 để biến thanh cuộn nội dung nghe nhìn thành trải nghiệm thanh cuộn tiêu đề gọn nhẹ.

Bắt đầu với mã đánh dấu hỗ trợ tiếp cận

Trình cuộn nội dung nghe nhìn chỉ bao gồm một vài thành phần cốt lõi, một 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. Khi truy cập vào trang này, người dùng 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ị hình ảnh và chú thích của hình ảnh theo ngữ nghĩa:

<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 thanh cuộn nội dung nghe nhìn là một cơ hội trải nghiệm người dùng để giúp cung cấp thêm ngữ 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 hoặc cung cấp giao diện người dùng bằng lời 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 Năm quy tắc vàng về văn bản thay thế tuân thủ.

Thuộc tính loading chấp nhận từ khoá lazy để 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 nhìn. Đ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 xuống cho các mục mà họ đã cuộn vào chế độ xem.

Hỗ trợ bảng phối màu mà người dùng ưu tiên

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ó cả kiểu giao diện người dùng sáng và tối. Đây là chế độ tối hoặc chế độ sáng miễn phí, tuỳ thuộc vào cách bạn xem:

<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 canvas mặc định tối 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 điều hướng giữa các trang của trang web sẽ không hiển thị nền canvas 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ễ nhìn hơn nhiều.

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 trên của ul > li > a > figure > picture > img, 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 hình ảnh và văn bản giữ chỗ tĩnh, nhưng bạn có thể sử dụng nguồn dữ liệu yêu thích của mình.

Thêm kiểu bằng CSS

Bây giờ, CSS sẽ 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ửa hàng ứng dụng và nhiều trang web và ứng dụng khác sử dụng các khu vực cuộn ngang để đóng gói khung nhìn bằng các danh mục và tuỳ chọn.

Tạo bố cục thanh cuộn

Điều quan trọng là bạn phải tránh cắt nội dung trong bố cục hoặc cắt bớt văn bản bằng dấu ba chấm. Nhiều TV có thanh cuộn nội dung đa phương tiện giống như thanh cuộn này, nhưng thường phải dùng đến dấu ba chấm để hiển thị nội dung. Bố cục này không! Tính năng này cũng cho phép nội dung đa phương tiệ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 hiển thị. Một trong hai không có dấu ba chấm, nghĩa là nó cao hơn và mỗi tiêu đề đều có thể đọc được. Tiêu đề còn lại 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 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ẽ sử dụng thuộc tính tuỳ chỉnh này để tạo tỷ lệ khung hình cơ sở: 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 có thể hoàn tất phần cơ bản của thanh 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);
  }
}

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

Tuy nhiên, các hình ảnh chưa thích ứng và bị tràn ra khỏi hộp chứa. Hãy điều chỉnh các thành phần này bằng một số kích thước, kiểu dáng vừa vặn và đườ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 khu vực cuộn từ cạnh này sang cạnh kia, là yếu tố quan trọng để tạo ra một thành phần hài hòa và tối giản.

Để hoàn thành bố cục cuộn từ cạnh này sang cạnh kia, căn chỉnh với kiểu chữ và các dò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 */
}

Sửa lỗi khoảng đệm cuộn theo chiều ngang Phần trên cho thấy việc thêm khoảng đệm cho vùng chứa cuộn rất dễ dàng, nhưng vẫn có các vấn đề về khả năng tương thích với vùng chứa này (mặc dù đã được khắc phục trong Chromium 91 trở lên!). Hãy xem nội dung tại đây để biết một chút về quá trình phát triển, nhưng tóm lại là khoảng đệm không phải lúc nào cũng được tính đến trong thành phần hiển thị cuộn.

Một hộp được làm nổi bật ở phía cuối cù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 căn chỉnh mong muốn.

Để lừa trình duyệt đặt khoảng đệm ở cuối thanh cuộn, tôi sẽ nhắm đến hình cuối cùng trong mỗi danh sách và thêm một phần tử giả là 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 thanh cuộn nội dung nghe nhìn hoạt động ở bất kỳ chế độ ghi nào và hướng tài liệu.

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

Vùng chứa cuộn có vùng tràn có thể trở thành khung nhìn chụp nhanh 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 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;

  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

Cảm hứng cho thành phần này đến từ sự 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 thanh cuộn nội dung nghe nhìn rất giống với thanh cuộn này làm bố cục màn hình chính chính. Tiêu điểm là một khoảnh khắc trải nghiệm người dùng rất quan trọng, chứ không chỉ là một điểm bổ sung nhỏ. Hãy tưởng tượng bạn sử dụng thanh cuộn nội dung nghe nhìn này trên ghế sofa bằng điều khiển từ xa, hãy cung cấp một số điểm cải tiến nhỏ 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 sẽ đặt kiểu đường viền tiêu điểm 7px ra khỏi hộp, tạo ra một khoảng trống đẹp mắt. Nếu người dùng không có lựa chọn ưu tiên về chuyển động để giảm chuyển động, thì độ dời sẽ được chuyển đổi, tạo chuyển động nhẹ nhàng cho sự kiện lấy tiêu điểm.

Chỉ mục di chuyển

Người dùng tay điều khiển trò chơi và bàn phím cần được chú ý đặc biệt trong các danh sách dài này về nội dung và tuỳ chọn cuộn. Mẫu phổ biến để giải quyết vấn đề này được gọi là chỉ mục di chuyển. Đó 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 mỗi lần chỉ cho phép 1 phần tử con giữ tiêu điểm. Trải nghiệm một mục có thể lấy tiêu điểm tại một thời điểm này được thiết kế để cho phép bỏ qua danh sách các mục có thể dài, thay vì nhấn phím tab hơn 50 lần để đến cuối.

Có 300 mục trong thanh cuộn đầu tiên của bản minh hoạ đó. Chúng ta có thể làm tốt hơn là buộc các phần tử này phải di chuyển qua tất cả các phần tử để đến phần tiếp theo.

Để tạo trải nghiệm này, JavaScript cần quan sát các sự kiện bàn phím và sự kiện tâm đ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 nó cho 3 thanh cuộn:

import {rovingIndex} from 'roving-ux';

rovingIndex({
  element: someElement
});

Bản minh hoạ này truy vấn tài liệu cho các thanh cuộn và cho mỗi thanh cuộn, gọi hàm rovingIndex(). Truyền phần tử rovingIndex() để 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 mục tiêu, trong trường hợp mục tiêu 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, tính năng hỗ trợ aspect-ratio đang ở trạng thái bật trong Firefox nhưng có trong trình duyệt Chromium hoặc hộp set-top. Vì bố cục lưới của thanh 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 bên trong truy vấn nội dung nghe nhìn có tính năng kiểm tra khả năng hỗ trợ tỷ lệ khung hình. Cải tiến dần vào một số trình cuộn nội dung nghe nhìn linh động 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 sử 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 của trình cuộn nội dung đa phương tiện sẽ được nâng cấp lên kích thước aspect-ratio. Khi sử dụng cú pháp lồng ghép 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 đầu, hàng thứ hai hay hàng thứ ba. Cú pháp lồng cũng cho phép thiết lập một số điều chỉnh nhỏ đối với khung nhìn, ngay tại đó với logic định cỡ khác.

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

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

Mặc dù kỹ thuật tiếp theo này chỉ có sẵn dưới dạng cờ trong Canary, nhưng tôi muốn chia sẻ cách tiết kiệm đáng kể thời gian tải trang và mức sử dụng dữ liệu 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 không, chẳng hạn như chế độ 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, ẩ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ể xem nội dung mà không phải tốn chi phí tải các hình ảnh nặng xuống. Dưới đâ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 nhìn (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 khoảng 80% và 10% dữ liệu qua mạng. CSS khá mạnh mẽ.

Kết luận

Giờ thì bạn đã biết cách tôi làm, còn bạn thì sao?! 🙂

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 Codepen hoặc lưu trữ bản minh hoạ của riêng bạn, tweet cho tôi về bản minh hoạ đó và 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!