构建媒体滚动条组件

简要介绍如何为电视、手机、桌面设备等设备构建自适应水平滚动视图。

在这篇博文中,我想分享一些办法,为 Web 打造水平滚动体验,即尽可能精简、响应迅速、易于访问且适用于各种浏览器和平台(例如电视!)的横向滚动体验。查看演示

演示

如果你更喜欢视频,可以参考本博文的 YouTube 版本:

概览

我们将构建一个横向滚动布局,用于托管媒体或产品缩略图。该组件最初是一个不起眼的 <ul> 列表,但通过 CSS 转换为令人满意且流畅的滚动体验,展示图片并将其贴靠到网格。添加了 JavaScript 以促进流动索引互动,帮助键盘用户跳过遍历 100 多个项。此外,还使用实验性媒体查询 prefers-reduced-data,将媒体滚动条转换为轻量级的标题滚动条体验。

从可访问的标记开始

媒体滚动条仅由几个核心组件(一个包含内容项的列表)组成。列表最简单的形式是可以遍及全球的,可以被所有人明确地使用。到达此页面的用户可以浏览列表并点击链接查看商品。这是我们无障碍的基地。

提供包含 <ul> 元素的列表:

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

使用 <a> 元素使列表项具有互动性:

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

使用 <figure> 元素在语义上表示图像及其图片说明:

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

请注意 <img> 上的 altloading 属性。媒体滚动条的替代文本是一种用户体验机会,有助于为缩略图提供额外的背景信息;如果图片未加载,则用作后备文本;或者,它可以为依赖屏幕阅读器等辅助技术的用户提供语音界面。如需了解详情,请参阅确保合规替代文本的五大黄金法则

loading 属性接受关键字 lazy,作为指明应仅在图片位于视口内时提取此图片来源的一种方式。这对于大型列表非常有帮助,因为用户只会下载他们滚动到视图中的内容的图片。

支持用户的配色方案偏好设置

使用 color-scheme 作为 <meta> 标记,以指示浏览器您的网页需要提供的浅色和深色用户代理样式。它是一种免费的深色模式或浅色模式,具体取决于您查看的效果:

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

元标记会尽可能提供最早的信号,因此如果用户偏好深色主题,浏览器可以选择深色的默认画布颜色。这意味着,在网站的页面之间导航时,不会在加载之间闪烁白色画布背景。在加载间隔期间提供无缝的深色主题,使视觉体验更加美观。

如需了解详情,请访问 Thomas Steiner,网址为 https://web.dev/color-scheme/

添加内容

鉴于上述 ul > li > a > figure > picture > img 内容结构,下一个任务是添加要滚动浏览的图片和标题。虽然演示中已经添加了静态占位符图片和文本,但您也可以随时使用您喜爱的数据源进行演示。

使用 CSS 添加样式

现在,是时候 CSS 将这个通用的内容列表转换成一种体验了。Netflix、App Store 等许多网站和应用会使用水平滚动区域为视口填充各种类别和选项。

创建滚动条布局

请务必避免截断布局中的内容,或避免过度使用带有省略号的文字被截断。许多电视机都配有与此类似的媒体滚动条,但往往会采用省略号内容。此布局无法做到这一点! 它还允许媒体内容替换列大小,从而使 1 个布局足够灵活,可以处理许多有趣的组合。

显示了 2 个滚动行。其中一个标题不带省略号,这意味着它稍高一些,且每个标题都完全清晰可辨。另一个标题较短,许多标题以省略号截断。

借助容器,您可以通过以自定义属性的形式提供默认大小来替换列大小。此网格布局调整了列大小,只管理间距和方向:

.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;
}

然后,<picture> 元素使用该自定义属性来创建基本宽高比:方框:

.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);
  }
}

只需再设置几个小的样式,即可完成媒体滚动条的实现:

.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);
  }
}

设置 overflow 会将 <ul> 设置为允许滚动和键盘导航浏览其列表,然后通过获取新的显示类型 inline-block 来移除每个直接子级 <li> 元素的 ::marker

不过,这些图片还没有响应,会从它们所在的盒子里突然跳出。使用一些尺寸、适合度、边框样式以及背景渐变进行加载,以便在延迟加载时适应这些加载条件:

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%)
    );
}

滚动内边距

与网页内容对齐,再加上无边框滚动界面区域,对于实现和谐且最小的组件至关重要。

为了实现与我们的排版和布局线对齐的全屏滚动布局,请使用与 scroll-padding 匹配的 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 */
}

水平滚动内边距 bug 修复 上图显示了填充滚动容器应该有多么容易,但该容器存在尚未解决的兼容性问题(不过,在 Chromium 91 及更高版本中已修复!)。请参阅此处了解一段历史记录,但简要说明:滚动视图中并不总是考虑内边距。

最后一个列表项的内嵌末端会突出显示一个框,显示内边距和元素的宽度与所需的对齐方式相同。

为了诱使浏览器将内边距放在滚动条的末尾,我将定位每个列表中的最后一个数字,并附加一个表示所需内边距量的伪元素。

.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);
  }
}

使用逻辑属性可让媒体滚动条以任何写入模式和文档方向运行。

滚动贴靠

带有溢出的滚动容器可以变成包含一行 CSS 的贴靠视口,然后由子代指定它们将如何与该视口对齐。

.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;
  }
}

侧重点

这个组件的灵感来自它在电视、App Store 等平台上的巨大受欢迎程度。许多视频游戏平台使用与这个非常相似的媒体滚动条作为主要主屏幕布局。重点在这里是一个重要的用户体验时刻,而不仅仅是一个小小的增加点。想象一下,您坐在沙发上和遥控器上使用此媒体滚动条,不妨对互动进行一些细微的改进:

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

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

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

这会将焦点轮廓样式 7px 设置为远离框,从而留出一些漂亮的空间。如果用户没有与减少动作有关的动作偏好,则偏移会发生转换,从而为焦点事件提供细微的动作。

流动索引

在这些长长的滚动内容和选项列表中,游戏手柄和键盘用户需要特别注意。解决此问题的常用模式称为“流动索引”。当一个项容器获得键盘焦点时,一次只允许 1 个子项保持焦点。一次点击一个可聚焦项的功能旨在绕过可能很长的项列表,而不是按 Tab 键 50 次以上来完成。

演示的第一个滚动条中有 300 个项。不如让它们遍历所有部分以到达下一部分,这是更好的方法。

为了打造这种体验,JavaScript 需要观察键盘事件和焦点事件。我在 npm 上创建了一个小型开源库,以帮助轻松实现这种用户体验。下面说明了如何将其用于 3 个滚动条:

import {rovingIndex} from 'roving-ux';

rovingIndex({
  element: someElement
});

此演示在文档中查询滚动条,并针对每个滚动条调用 rovingIndex() 函数。向 rovingIndex() 传递该元素以获取漫游体验,例如列表容器和目标查询选择器(如果焦点目标不是直接后代)。

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

如需详细了解此效果,请参阅开源库 roving-ux

宽高比

在撰写本文时,aspect-ratio 的支持在 Firefox 中由标志提供,但在 Chromium 浏览器或机顶盒中可用。由于媒体滚动条网格布局仅指定方向和间距,因此在媒体查询(该功能会检查是否支持宽高比)内,大小可能会发生变化。对一些更具动态性的媒体滚动条进行渐进式增强。

宽高比为 4:4 的方框显示在其他采用 16:9 和 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);
      }
    }
  }
}

如果浏览器支持 aspect-ratio 语法,则媒体滚动条图片会升级为 aspect-ratio 大小。使用草稿嵌套语法,每张图片都可以根据其是第一行、第二行还是第三行来更改宽高比。借助嵌套语法,您还可以利用其他大小调整逻辑直接对视口进行一些细微调整。

有了该 CSS,当该功能适用于更多浏览器引擎时,布局将呈现一个易于管理但视觉吸引力更强的布局。

希望减少数据流量

虽然下一种技术仅在 Canary 中的标志后面可用,但我想和您分享一下如何通过几行 CSS 节省大量网页加载时间和数据使用。级别 5 中的 prefers-reduced-data 媒体查询允许询问设备是否处于任何减少的数据状态,例如流量节省程序模式。如果是,我可以修改文档 在本例中,隐藏图片

ALT_TEXT_HERE

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

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

内容仍然可浏览,但没有下载繁重图片的费用。以下是添加 prefers-reduced-data CSS 之前的网站:

(7 个请求,131 毫秒内 100kb 的资源)

ALT_TEXT_HERE

以下是添加 prefers-reduced-data CSS 后的网站性能:

ALT_TEXT_HERE

(71 个请求,1.07 秒内使用了 1.2 MB 的资源)

减少 64 次请求,这样,此浏览器标签页的视口内大约有 60 张图片(针对宽屏显示屏进行的测试),网页加载速度提升约 80%,通过网络传输的数据将增加 10%。功能相当强大。

总结

现在你已经知道我是怎么做的,你知道该怎么做呢?!🙂

下面,我们就来介绍一下我们的方法多样化,并了解在 Web 上构建网站的所有方法。 创建一个 Codepen 或托管自己的演示,在 Twitter 上向我提出,我会将其添加到下面的“社区混剪”部分。

来源

社区混剪作品

此处尚无可显示的内容!