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

通过声明滚动贴靠位置,打造可控的滚动体验。

Robert Flack
Robert Flack
Majid Valipour
Majid Valipour

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

背景

滚动贴靠的情况

滚动是一种与网页内容互动的自然热门方式。它是平台原生方式,可让用户访问的信息无法同时显示在屏幕上,在屏幕空间有限的移动平台上尤为重要。因此,网页作者越来越喜欢将内容整理到可滚动的平面列表中,而不是采用深层次的层次结构。

滚动的主要缺点是不够精确。滚动操作很少最终与段落或句子对齐。对于具有有意义的边界的分页或分项内容,当滚动结束于页面或图片中间,使页面部分可见时,这种情况会更明显。这些用例受益于可控性的滚动体验。

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

借助 CSS Scroll Snap,网页开发者可以用边界标记每个滚动容器,以便在达到滚动操作终点时执行。然后,浏览器会根据滚动操作的具体情况、滚动容器的布局和可见性以及贴靠位置的详细信息选择最合适的结束位置,然后平稳地向该结束位置添加动画效果。回到之前的例子,当用户完成滚动浏览轮播界面时,其可见图片会卡入到位。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 属性,可用于调整滚动容器(或贴靠)的有效可视区域,在计算滚动贴靠对齐方式时使用。该属性用于定义相对于滚动容器内边距框的边衬区。在我们的示例中,顶部添加了 15vh 的额外边衬区,以指示浏览器将较低的位置(即滚动容器上边缘下方的 15vh)视为滚动贴靠的垂直开始边缘。贴靠时,贴靠目标元素的起始边缘将随这个新位置一起对齐,从而在上方留出空间。

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

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

观看演示 | 来源

与其他滚动 API 交互

DOM Scrolling API

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

流畅滚动

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

滚动行为

Overscroll 行为 API 可控制滚动在多个元素之间如何串联,并且不受滚动贴靠的影响。

注意事项和最佳做法

当目标元素间隔很大时,请避免使用强制性的贴靠功能。这可能会导致无法访问贴靠位置之间的内容。

在许多情况下,您可以将滚动贴靠功能添加为增强功能,而无需进行功能检测。如果需要,请使用 @supportsCSS.supports 检测对 CSS Scroll Snap 的支持。 请避免使用 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()