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

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

Robert Flack
Robert Flack
Majid Valipour
Majid Valipour

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

背景

滚动捕获的用例

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

滚动的主要缺点是缺乏精确性。滚动到最后时,很少会与段落或句子对齐。对于具有有意义边界的分页或分项内容,这种情况更为明显,因为当滚动到页面或图片中间时,内容会部分显示。这些用例可受益于良好控制的滚动体验。

Web 开发者长期以来一直依赖基于 JavaScript 的解决方案来控制滚动,以帮助解决这一缺点。但是,基于 JavaScript 的解决方案由于缺少滚动自定义基元或无法访问复合滚动,因此无法提供完整保真度解决方案。CSS 滚动贴靠可确保提供快速、高保真度且易于使用的解决方案,并在各浏览器中保持一致的效果。

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

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

CSS 滚动捕获

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

滚动容器可以通过使用 scroll-snap-type 属性选择启用滚动捕获。这会告知浏览器,它应考虑将此滚动容器贴靠到其子项生成的贴靠位置。scroll-snap-type 用于确定滚动轴(xyboth)和贴靠严格性(mandatoryproximity)。我们稍后会详细介绍这些功能。

您可以通过在元素上声明所需的对齐方式来产生对齐位置。此位置是滚动偏移量,其中最近的祖先滚动容器和元素会根据为给定轴指定的对齐方式进行对齐。每个轴都可以采用以下对齐方式:startendcenter

start 对齐表示滚动容器 snapport 的起始边缘应与元素 snap 区域的起始边缘对齐。同样,endcenter 对齐方式意味着滚动容器 snapport 的端边缘或中心应与元素 snap 区域的端边缘或中心对齐。

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

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

滚动捕获的常见用例是图片轮播界面。例如,若要创建一个水平图片轮播界面,使其在您滚动时自动跳转到每张图片,我们可以指定滚动容器在水平轴上具有强制性 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”一词。这是有意为之,因为它们实际上会修改所有相关滚动操作的边框,而不仅仅是滚动捕获。例如,在计算 PageDown 和 PageUp 等分页滚动操作的页面大小时,Chrome 会将这些边距考虑在内;在计算 Element.scrollIntoView() 操作的滚动量时,Chrome 也会将这些边距考虑在内。

查看演示 | 来源

与其他滚动 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. 使用 scroll-start新的 CSS API
  3. 处理 snapChanged()新的 JS 事件