构建 Stories 组件

关于如何在 Web 上构建类似于 Instagram Stories 体验的基础概述。

在这篇文章中,我想分享关于为 Web 构建 Stories 组件的思考,该组件是响应式的,支持键盘导航,并且可在各种浏览器中运行。

演示

如果您希望亲身体验构建此 Stories 组件,请查看 Stories 组件 Codelab

如果您更喜欢视频,这里是这篇文章的 YouTube 版本

概述

Stories UX 的两个流行示例是 Snapchat Stories 和 Instagram Stories(更不用说 fleets)。通常,在 UX 术语中,Stories 通常是仅限移动设备的、以点击为中心的模式,用于导航多个订阅。例如,在 Instagram 上,用户打开朋友的 Story 并浏览其中的图片。他们通常一次浏览许多朋友的 Story。通过点击设备的右侧,用户可以跳到该朋友的下一个 Story。通过向右滑动,用户可以跳到不同的朋友。Story 组件与轮播非常相似,但允许导航多维数组而不是一维数组。这就像每个轮播内部都有一个轮播。🤯

Visualized multi-dimensional array using cards. Left to right is a stack of purple borders cards, and inside each card is 1-many cyan bordered cards. List in a list.
第一个朋友轮播
第二个“堆叠式”故事轮播
👍 列表中的列表,又名:多维数组

为工作选择合适的工具

总而言之,我发现由于一些关键的 Web 平台功能,构建此组件非常简单。让我们来介绍一下它们!

CSS Grid

我们的布局对于 CSS Grid 来说并非难事,因为它配备了一些强大的内容处理方法。

朋友布局

我们的主要 .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 DevTools 的 设备模式 来突出显示 Grid 创建的列

让我们分解一下 grid 布局

  • 我们使用 100vh100vw 在移动设备上显式填充视口,并在桌面设备上约束大小
  • / 分隔我们的行和列模板
  • auto-flow 转换为 grid-auto-flow: column
  • 自动流模板是 100%,在这种情况下,它是滚动窗口宽度

在手机上,将其视为行大小为视口高度,每列为视口宽度。继续以 Snapchat Stories 和 Instagram Stories 为例,每列将是一个朋友的 Story。我们希望朋友的 Stories 继续在视口之外,以便我们有滚动到的位置。Grid 将创建布局 HTML 所需的列数,用于每个朋友的 Story,从而为我们创建一个动态且响应式的滚动容器。Grid 使我们能够集中整个效果。

堆叠

对于每个朋友,我们需要他们的 Stories 处于可分页状态。为了准备动画和其他有趣的模式,我选择了一个堆栈。当我说堆栈时,我的意思是像您俯视三明治一样,而不是像您从侧面看一样。

使用 CSS Grid,我们可以定义一个单单元格网格(即正方形),其中行和列共享一个别名 ([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% 进行框校正。父网格已经定义了 Story 图片视口的大小,因此无需告诉任何这些 Story 组件来填充它!

CSS 滚动捕捉点

CSS 滚动捕捉点规范 使在滚动时将元素锁定到视口中变得轻而易举。在这些 CSS 属性存在之前,您必须使用 JavaScript,而且……至少可以说很棘手。查看 Sarah Drasner 的 Introducing 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;
}
具有 overscroll 的父级定义了捕捉行为。
子级
.user {
  display: grid;
  grid: [story] 1fr / [story] 1fr;
  scroll-snap-align: start;
  scroll-snap-stop: always;
}
子级选择成为捕捉目标。

我选择滚动捕捉点有几个原因

  • 免费无障碍功能。滚动捕捉点规范指出,默认情况下,按 向左箭头向右箭头 键应在捕捉点之间移动。
  • 不断增长的规范。滚动捕捉点规范一直在获得新功能和改进,这意味着我的 Stories 组件可能只会从现在开始变得更好。
  • 易于实现。滚动捕捉点实际上是为以触摸为中心的水平分页用例而构建的。
  • 免费的平台风格惯性。每个平台都将以其风格滚动和停止,而不是规范化的惯性,后者可能具有奇怪的滚动和停止风格。

跨浏览器兼容性

我们在 Opera、Firefox、Safari 和 Chrome 以及 Android 和 iOS 上进行了测试。以下是我们在功能和支持方面发现差异的 Web 功能的简要概述。

尽管如此,我们确实有一些 CSS 没有应用,因此某些平台目前错失了 UX 优化。我很高兴不需要管理这些功能,并且相信它们最终会到达其他浏览器和平台。

scroll-snap-stop

轮播是促使创建 CSS 滚动捕捉点规范的主要 UX 用例之一。与 Stories 不同,轮播并不总是需要在用户与之交互后停在每张图像上。快速循环浏览轮播可能很好或受到鼓励。另一方面,Stories 最好逐个导航,而这正是 scroll-snap-stop 提供的。

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

在撰写本文时,scroll-snap-stop 仅在基于 Chromium 的浏览器上受支持。查看 浏览器兼容性 以获取更新。但这并不是障碍。这只是意味着在不受支持的浏览器上,用户可能会意外跳过朋友。因此,用户只需更加小心,否则我们需要编写 JavaScript 以确保未被跳过的朋友不会被标记为已查看。

如果您有兴趣,请在 规范中阅读更多内容。

overscroll-behavior

您是否曾经在滚动模态框时,突然开始滚动模态框后面的内容?overscroll-behavior 让开发者可以捕获该滚动,并且永远不会让它离开。它在各种情况下都很好用。我的 Stories 组件使用它来防止其他滑动和滚动操作离开组件。

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

Safari 和 Opera 是两个不 支持 它的浏览器,这完全没问题。这些用户将获得他们习惯的 overscroll 体验,并且可能永远不会注意到这种增强功能。我个人是忠实粉丝,并且喜欢将其作为我实现的几乎每个 overscroll 功能的一部分。这是一个无害的补充,只会带来改进的 UX。

scrollIntoView({behavior: 'smooth'})

当用户点击或单击并已到达朋友的故事集的末尾时,就该移动到滚动捕捉点集中的下一个朋友了。使用 JavaScript,我们能够引用下一个朋友并请求将其滚动到视图中。对此基本功能的支持非常出色;每个浏览器都将其滚动到视图中。但是,并非每个浏览器都以 'smooth' 的方式执行此操作。这只是意味着它被滚动到视图中而不是捕捉到视图中。

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

Safari 是唯一不支持此处 behavior: 'smooth' 的浏览器。查看 浏览器兼容性 以获取更新。

动手实践

现在您知道我是如何做到的,您会如何做呢?让我们使我们的方法多样化,并学习在 Web 上构建的所有方法。创建一个 Glitch在 Twitter 上给我发推文 您的版本,我将把它添加到下面的 社区混音 部分。

社区混音