构建“短片故事”组件

简要介绍了如何在 Web 上打造类似于 Instagram 快拍的体验。

在这篇博文中,我想分享有关构建故事网络组件的想法,该组件应具有自适应性、支持键盘导航,并且可在各种浏览器中运行。

演示

如果您希望亲自演示如何构建此 Stories 组件,请查看 Stories 组件 Codelab

如果您更喜欢视频,可以观看此帖子的 YouTube 版本:

概览

故事用户体验的两个热门示例是 Snapchat 故事和 Instagram 故事(更不用说推文)。 从一般用户体验的角度来看,短片故事通常是一种仅限移动设备使用的以点按为中心的模式,用于浏览多个订阅。例如,在 Instagram 上,用户打开好友的动态并浏览其中的照片。他们通常会一次性向多位好友发送邀请。用户只需点按设备的右侧,即可跳到相应好友的下一个故事。用户可以通过向右滑动跳到其他好友。 故事组件与轮播界面非常相似,但允许浏览多维数组,而不是一维数组。就像每个轮播界面中都有一个轮播界面一样。🤯

使用卡片直观呈现多维数组。从左到右是一叠紫色边框卡片,每张卡片内有 1 到多个青色边框卡片。列表中的列表。
第 1 个好友轮播界面
第 2 个“堆叠式”故事轮播界面
👍 列表中的列表,也称为:多维数组

选择合适的工具来完成工作

总而言之,得益于一些关键的 Web 平台功能,我发现这个组件的构建过程非常简单。我们来介绍一下这些功能!

CSS 网格

事实证明,CSS 网格可以轻松应对我们的布局,因为它配备了一些强大的内容管理方式。

好友布局

我们的主要 .stories 组件封装容器是移动优先的水平滚动视图:

.stories {
  inline-size: 100vw;
  block-size: 100vh;

  display: grid;
  grid: 1fr / auto-flow 100%;
  gap: 1ch;

  overflow-x: auto;
  scroll-snap-type: x mandatory;
  overscroll-behavior: contain;
  touch-action: pan-x;
}

/* desktop constraint */
@media (hover: hover) and (min-width: 480px) {
  max-inline-size: 480px;
  max-block-size: 848px;
}
使用 Chrome 开发者工具的设备模式突出显示由网格创建的列

我们来详细了解一下 grid 布局:

  • 我们使用 100vh100vw 在移动设备上明确填充视口,并在桌面设备上限制大小
  • / 用于分隔行和列模板
  • auto-flow 解析为 grid-auto-flow: column
  • 自动流式布局模板为 100%,在本例中为滚动窗口的宽度

在手机上,您可以将行大小视为视口高度,将每个列视为视口宽度。继续以 Snapchat 故事和 Instagram 故事为例,每个列将代表一个好友的故事。我们希望好友故事在视口外继续显示,以便用户可以滚动浏览。 网格将根据需要创建任意数量的列来布局每个好友故事的 HTML,从而为我们创建一个动态且自适应的滚动容器。借助网格,我们可以集中处理整个效果。

堆叠

对于每个好友,我们需要其故事处于可分页状态。为了准备动画和其他有趣的图案,我选择了一个堆栈。 我说堆叠,是指像俯视三明治一样,而不是从侧面看。

借助 CSS 网格,我们可以定义一个单单元格网格(即正方形),其中行和列共享一个别名 ([story]),然后将每个子项分配给该别名单单元格空间:

.user {
  display: grid;
  grid: [story] 1fr / [story] 1fr;
  scroll-snap-align: start;
  scroll-snap-stop: always;
}
.story {
  grid-area: story;
  background-size: cover;
  
}

这样一来,我们的 HTML 就可以控制堆叠顺序,同时还能让所有元素保持在流中。请注意,我们无需对 absolute 定位或 z-index 执行任何操作,也无需使用 height: 100%width: 100% 进行正确的框定。父网格已定义故事图片视口的大小,因此无需告知任何故事组件填充该视口!

CSS 滚动贴靠点

借助 CSS 滚动贴靠点规范,您可以轻松地在滚动时将元素锁定到视口中。在这些 CSS 属性出现之前,您必须使用 JavaScript,这至少可以说非常棘手。如需详细了解如何使用滚动捕捉点,请参阅 Sarah Drasner 撰写的介绍 CSS 滚动捕捉点一文。

不含 scroll-snap-points 样式的横向滚动和含 scroll-snap-points 样式的横向滚动。 如果没有此设置,用户可以像往常一样自由滚动。使用此功能后,浏览器会轻轻地停留在每个商品上。
父级
.stories {
  display: grid;
  grid: 1fr / auto-flow 100%;
  gap: 1ch;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  overscroll-behavior: contain;
  touch-action: pan-x;
}
具有滚动回弹的父级定义了贴靠行为。
儿童
.user {
  display: grid;
  grid: [story] 1fr / [story] 1fr;
  scroll-snap-align: start;
  scroll-snap-stop: always;
}
儿童选择成为贴靠目标。

我选择滚动吸附点有以下几个原因:

  • 免费无障碍功能。滚动捕捉点规范指出,按向左键向右键应默认在各个捕捉点之间移动。
  • 不断发展的规范。滚动捕捉点规范一直在不断推出新功能和改进,这意味着我的 Stories 组件可能会越来越好。
  • 易于实施。从实际来看,滚动捕捉点是为以触控为主的横向分页使用情形而构建的。
  • 自由平台风格的惯性。每个平台都会以自己的风格滚动和停止,而标准化惯性可能会产生怪异的滚动和停止风格。

跨浏览器兼容性

我们测试了 Opera、Firefox、Safari 和 Chrome,以及 Android 和 iOS。以下简要介绍了我们在功能和支持方面发现差异的网页版功能。

不过,我们确实有一些 CSS 未应用,因此部分平台目前无法获得 UX 优化。我确实很喜欢无需管理这些功能,并且相信它们最终会登陆其他浏览器和平台。

scroll-snap-stop

轮播界面是促使创建 CSS Scroll Snap Points 规范的主要用户体验应用场景之一。与故事不同,轮播界面不一定需要在用户与之互动后停留在每张图片上。快速浏览轮播界面可能没问题,甚至受到鼓励。另一方面,故事最好逐个浏览,而这正是 scroll-snap-stop 所提供的功能。

.user {
  scroll-snap-align: start;
  scroll-snap-stop: always;
}

在撰写本文时,scroll-snap-stop 仅适用于基于 Chromium 的浏览器。请查看浏览器兼容性以了解最新信息。不过,这不会阻止您使用该功能。这只是意味着,在不受支持的浏览器上,用户可能会意外跳过某个好友。因此,用户只需更加小心,或者我们需要编写 JavaScript 来确保跳过的朋友不会被标记为已查看。

如果您感兴趣,可以参阅规范了解详情。

overscroll-behavior

您是否曾在滚动浏览某个模态框时,突然开始滚动模态框后面的内容? overscroll-behavior 可让开发者捕获该滚动事件,并阻止其离开。适合各种场合。“我的故事”组件使用它来防止额外的滑动和滚动手势离开组件。

.stories {
  overflow-x: auto;
  overscroll-behavior: contain;
}

Safari 和 Opera 是不支持此功能的 2 个浏览器,这完全没问题。这些用户将获得他们已经习惯的过滚动体验,可能永远不会注意到此增强功能。我个人非常喜欢它,几乎在实现的每个过度滚动功能中都包含它。这是一项无害的添加,只会带来更好的用户体验。

scrollIntoView({behavior: 'smooth'})

当用户点按或点击并已到达好友故事集的末尾时,就该移至滚动捕捉点集中的下一位好友了。借助 JavaScript,我们能够引用下一个好友,并请求将其滚动到视图中。对这些基本功能的支持非常出色;每个浏览器都将其滚动到视图中。但并非所有浏览器都支持此功能 'smooth'。这只是意味着它会滚动到视图中,而不是贴靠。

element.scrollIntoView({
  behavior: 'smooth'
})

Safari 是唯一不支持此处 behavior: 'smooth' 的浏览器。如需了解最新信息,请参阅浏览器兼容性

实操

现在您已经知道我是如何做到的,那么您会怎么做呢?让我们丰富方法,了解在 Web 上构建的所有方式。创建 Glitch在 Twitter 上私信我您的版本,我会将其添加到下方的社区混音部分。

社区混音作品