构建“短片故事”组件

基础性地概述了如何在 Web 上构建类似于 Instagram 快拍的体验。

在本文中,我想分享关于为 Web 构建快拍组件的想法,该组件具有自适应性、支持键盘导航,并且可在各种浏览器中运行。

演示

如果您希望通过实际操作演示自行构建此“快拍”组件,请参阅“快拍”组件 Codelab。

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

概览

Snapchat Stories 和 Instagram Stories(更不用说 Fleets)就是两个广受欢迎的“快拍”用户体验示例。 从一般用户体验的角度来看,故事通常是一种仅限移动设备、以点按为中心的多订阅内容浏览模式。例如,在 Instagram 上,用户会打开好友的快拍,并浏览其中的图片。他们通常会一次性邀请很多好友。点按设备右侧,用户可以跳到该好友的下一个故事。用户可以通过向右滑动,跳到另一位好友。 故事组件与轮播界面非常相似,但允许浏览多维数组,而不是一维数组。就像每个轮播界面中 都包含一个轮播界面。🤯

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

根据任务选用合适的工具

总而言之,我发现这个组件非常容易构建,这要归功于一些关键的 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 布局:

  • 我们使用 100vh 和 100vw 明确填充移动设备上的视口,并限制桌面设备上的尺寸
  • / 用于分隔行模板和列模板
  • auto-flow 翻译为grid-auto-flow: column
  • 自动流模板为 100%,在本例中,该模板为滚动窗口宽度

在手机上,您可以将行大小视为视口高度,将每列视为视口宽度。继续以 Snapchat 故事和 Instagram 故事为例,每列都是好友的故事。我们希望好友动态在视口之外继续显示,这样我们就有可以滚动到的位置。 网格会根据需要创建任意数量的列,以便为每个好友故事布局 HTML,从而为我们创建一个动态且自适应的滚动容器。Grid 让我们能够集中管理整个效果。

叠加

对于每个好友,我们需要将其故事设置为分页就绪状态。 为了准备动画和其他有趣的图案,我选择了一个堆栈。 我说的堆叠,是指从上往下看,就像看一个三明治,而不是从侧面看。

借助 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 样式的横向滚动。 如果没有此属性,用户可以正常自由滚动。这样,浏览器就会在每个内容项上轻轻停留。
父级
.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 上进行了测试。下面简要介绍了我们发现功能和支持存在差异的 Web 功能。

不过,我们确实有一些 CSS 未应用,因此某些平台目前缺少用户体验优化。我确实很享受无需管理这些功能,并且相信它们最终会推广到其他浏览器和平台。

scroll-snap-stop

轮播界面是促使创建 CSS 滚动贴靠点规范的主要用户体验用例之一。与故事不同,轮播界面不一定需要在用户与其互动后停在每张图片上。快速循环浏览轮播界面可能没问题,甚至值得鼓励。另一方面,故事最好逐个浏览,而这正是 scroll-snap-stop 所提供的。

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

在撰写本文时,只有基于 Chromium 的浏览器支持 scroll-snap-stop。请查看浏览器兼容性,了解最新动态。不过,这并非阻碍因素。这只是意味着,在不受支持的浏览器上,用户可能会不小心跳过好友。因此,用户必须更加小心,否则我们就需要编写 JavaScript 来确保跳过的好友不会被标记为已查看。

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

overscroll-behavior

您是否曾遇到过这样的情况:在滚动浏览模态窗口时,突然开始滚动浏览模态窗口后面的内容? overscroll-behavior 允许开发者捕获该滚动操作,并使其永远不会离开。它适合各种场合。“我的故事”组件使用它来防止额外的滑动和滚动手势离开组件。

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

Safari 和 Opera 是两个不支持此功能的浏览器,这完全没问题。这些用户将获得他们习惯的滚动回弹体验,并且可能永远不会注意到这项增强功能。我个人非常喜欢这种效果,几乎在我实现的每个滚动回弹功能中都会加入这种效果。这是一种无害的添加,只会改善用户体验。

scrollIntoView({behavior: 'smooth'})

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

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

Safari 是唯一不支持 behavior: 'smooth' 的浏览器。请查看浏览器兼容性,了解最新动态。

实操训练

现在,您已经知道我是如何做到的,那么您会怎么做呢?!让我们采用多样化的方法,学习在 Web 上构建应用的所有方式。创建一个演示,在 Twitter 上向我发送您的版本,我会将其添加到下面的社区混音部分。

社区混剪