利用 CSS Scroll Snap,有效控制滚动

通过声明滚动卡顿位置,打造精心控制的滚动体验。

Robert Flack
Robert Flack
Majid Valipour
Majid Valipour

借助 CSS Scroll Snap 功能,网站开发者可以声明滚动贴靠位置,从而打造可控的滚动体验。分页文章和图片轮播界面就是这方面的两个常见示例。CSS Scroll Snap 提供了一个易于使用且一致的 API,用于构建这些热门的用户体验模式。

滚动是一种常用且自然的 Web 内容互动方式。这是平台提供的一种原生方式,可让用户访问比屏幕上一次可见的信息更多的信息,在屏幕空间有限的移动平台上尤为重要。因此,Web 作者越来越倾向于将内容整理成可滚动的扁平列表,而不是深层次结构。

滚动的主要缺点是缺乏精确性。在极少数情况下,滚动最终会与段落或句子对齐对于具有有意义的边界的分页或分项内容,这种情况更明显,当页面或图片中部滚动结束,并使其部分可见时,这种情况会更明显。这些用例可从良好控制的滚动体验中受益。

长期以来,Web 开发者一直依赖基于 JavaScript 的解决方案来控制滚动,以帮助解决这一缺陷。但是,由于缺少滚动自定义基元或对合成滚动的访问权限,基于 JavaScript 的解决方案无法提供全保真解决方案。CSS Scroll Snap 可确保快速、高保真且易于使用的解决方案,在各种浏览器中以一致的方式运行。

借助 CSS 滚动贴靠,网站作者可以为每个滚动容器标记边界,以便滚动操作在边界处结束。然后,浏览器会根据滚动操作的具体细节、滚动容器的布局和可见性以及固定位置的详细信息,选择最合适的结束位置,然后以流畅的动画效果滚动到该位置。回到前面的示例,当用户滚动完轮播界面时,其可见图片会贴靠到位。JavaScript 无需滚动调整。

将 CSS 滚动贴靠功能与图片轮播界面搭配使用示例。
将 CSS 滚动贴靠与图片轮播界面搭配使用的示例。 在这里,滚动捕获可确保在滚动结束时,图片的水平中心与滚动容器的水平中心对齐。

CSS 滚动捕获

滚动捕获是指在滚动操作完成后,调整滚动容器的滚动偏移量,使其位于首选的捕获位置

您可以使用 scroll-snap-type 属性为滚动容器选择启用滚动贴靠功能。这会告知浏览器应考虑将此滚动容器贴靠到其后代生成的贴靠位置。scroll-snap-type 可确定发生滚动的轴(xyboth),以及贴靠严格程度(mandatoryproximity)。我们稍后会详细介绍这些内容。

通过在元素上声明所需的对齐方式,可以生成贴靠位置。此位置是滚动偏移量,在该位置最近的祖先滚动容器和元素将按照为给定轴指定的方式对齐。每个轴上可采用以下对齐方式:startendcenter

start 对齐表示滚动容器贴靠区域的起始边缘应与元素贴靠区域的起始边缘对齐。同样,endcenter 对齐表示滚动容器贴靠的末端或中心应与元素贴靠区域的末端或中心齐平。

水平滚动轴上各种对齐方式的示例。

以下示例说明了如何使用这些概念。

滚动贴靠的常见用例是图片轮播。例如,若要创建一个水平图片轮播界面,使其在您滚动时自动跳转到每张图片,我们可以指定滚动容器在水平轴上具有强制性 scroll-snap-type。将每张图片设置为 scroll-snap-align: center,以确保自动跳转会将图片居中显示在轮播界面中。

#gallery {
  scroll-snap-type: x mandatory;
  overflow-x: scroll;
  display: flex;
}

#gallery img {
   scroll-snap-align: center;
}
<div id="gallery">
  <img src="cat.jpg">
  <img src="dog.jpg">
  <img src="another_cute_animal.jpg">
</div>

由于贴靠位置与元素相关联,因此贴靠算法可以根据元素和滚动容器大小智能地确定贴靠的时间和方式。例如,考虑一张图片大于轮播界面的情况。朴素的贴靠算法可能会阻止用户四处平移以查看完整图片。但规范要求实现能够检测这种情况,并允许用户在图片内自由滚动,但只贴靠图片的边缘。

观看演示 | 来源

示例:包含历程的商品页面

另一种可受益于滚动贴靠的常见情况是包含多个可垂直滚动的逻辑部分的页面,例如典型的商品页面。scroll-snap-type: y proximity; 更自然地适合这类情况。当用户滚动到特定版块的中间时,它不会干扰用户,但当用户滚动到足够近时,它会自动跳转到新版块并吸引用户注意力。

实现方式如下:

article {
  scroll-snap-type: y proximity;
  /* Reserve space for header plus some extra space for sneak peeking. */
  scroll-padding-top: 15vh;
  overflow-y: scroll;
}
section {
  /* Snap align start. */
  scroll-snap-align: start;
}
header {
  position: fixed;
  height: 10vh;
}
<article>
  <header> Header </header>
  <section> Section One </section>
  <section> Section Two </section>
  <section> Section Three </section>
</article>

滚动内边距和外边距

商品页面的顶部有固定的页眉。设计要求在滚动容器固定时,顶部部分仍保持可见,以便向用户提供有关上方内容的设计提示。

scroll-padding 属性是一种新的 CSS 属性,可用于调整滚动容器(也称为“snapport”)的有效可视区域,该区域在计算滚动贴靠对齐时会用到。该属性定义了滚动容器的内边距框的边衬区。在我们的示例中,顶部添加了 15vh 额外的边衬区,指示浏览器将位于滚动容器顶部边缘下方的较低位置 15vh 视为其用于贴靠滚动的垂直起始边缘。贴靠时,贴靠目标元素的起始边缘将与这个新位置齐平,从而在上方留出空间。

scroll-margin 属性用于定义用于调整吸附目标有效框的偏移量,类似于 scroll-padding 在吸附滚动容器上的作用方式。

您可能已经注意到,这两个属性中不含“snap”一词。这是有意为之,因为它们实际上会修改所有相关滚动操作的边框,而不仅仅是滚动捕获。例如,Chrome 在计算分页滚动操作(如 PageDown 和 PageUp)的页面大小时,以及计算 Element.scrollIntoView() 操作的滚动量时都会考虑这些因素。

查看演示 | 来源

与其他滚动 API 的互动

DOM 滚动 API

滚动捕获会在所有滚动操作(包括由脚本发起的操作)之后发生。当您使用 Element.scrollTo 等 API 时,浏览器将计算操作的预期滚动位置,然后应用适当的贴靠逻辑来查找最终的贴靠位置。因此,用户脚本无需针对贴靠执行任何手动计算。

平滑滚动

平滑滚动用于控制程序化滚动操作的行为,而滚动贴靠用于确定其目标位置。由于它们控制滚动的正交方面,因此可以一起使用和相互补充。

滚动行为

滚动回弹行为 API 用于控制滚动如何跨多个元素链式传递,并且不受滚动捕获的影响。

注意事项和最佳实践

当目标元素间隔较大时,避免使用强制贴靠。这可能会导致无法访问两点之间的内容。

在许多情况下,可以将滚动贴靠功能添加为增强功能,而无需进行功能检测。如果需要,请使用 @supportsCSS.supports 检测对 CSS 滚动捕获的支持。避免使用已废弃的规范中也存在的 scroll-snap-type

CSS 中的特征检测

@supports (scroll-snap-align: start) {
  article {
    scroll-snap-type: y proximity;
    scroll-padding-top: 15vh;
    overflow-y: scroll;
  }
}

JavaScript 中的功能检测

if (CSS.supports('scroll-snap-align: start')) {
  // use css scroll snap
} else {
  // use fallback
}

切勿假设以编程方式滚动 API(例如 Element.scrollTo)始终会在请求的滚动偏移量处完成。在程序化滚动完成后,贴靠滚动功能可能会调整滚动偏移量。请注意,即使在滚动到相应位置之前,这也不是一个好的假设,因为滚动可能会因其他原因而中断,但在滚动到相应位置时尤其如此。

后续工作

Chrome 团队最近开展的一项调查的重点是滚动体验。调查结果确定了多个方面需要采取额外措施来缩小插件库与 CSS 之间的差距。我们将重点关注 scroll-snap 的后续工作,包括:

  1. 各种浏览器的 API 可用性和兼容性。
  2. 使用新的 CSS API,例如 scroll-start
  3. 处理新的 JS 事件,例如 snapChanged()