实用 CSS 滚动捕捉

Avatar of Max Kohler
Max Kohler

DigitalOcean 为您的旅程各个阶段提供云产品。 立即开始使用 价值 200 美元的免费积分!

CSS 滚动捕捉可以让您在用户完成滚动后将视窗锁定到某些元素或位置。 它非常适合构建像这样的交互

实时演示

浏览器支持和基本用法

自从 CSS 滚动捕捉在 2016 年推出以来,其浏览器支持已大幅提升,Google Chrome (69+)、Firefox、Edge 和 Safari 都支持某种版本的滚动捕捉。

这些浏览器支持数据来自 Caniuse,其中包含更多详细信息。 数字表示浏览器从该版本开始支持该功能。

桌面

ChromeFirefoxIEEdgeSafari
696811*7911

移动设备 / 平板电脑

Android ChromeAndroid FirefoxAndroidiOS Safari
12712712711.0-11.2

滚动捕捉通过在容器元素上设置 scroll-snap-type 属性,并在其内部元素上设置 scroll-snap-align 属性来实现。 当容器元素滚动时,它将捕捉到您定义的子元素。 最基本的形式如下所示

<div class="container">
  <section class="child"></section>
  <section class="child"></section>
  <section class="child"></section>
  <p>...</p>
</div>
.container {
  scroll-snap-type: y mandatory;
}

.child {
  scroll-snap-align: start;
}

这与 规范的第一个版本不同,该版本允许您使用 repeat 关键字手动设置捕捉点

.container {
  /* OLD */
  scroll-snap-points-y: repeat(300px);
}

这种方法非常有限。 因为它只允许等间距的捕捉点,所以您无法真正构建一个捕捉到不同大小元素的界面。 如果元素在不同屏幕尺寸上改变形状,您也可能会遇到问题。

在撰写本文时,Firefox、Internet Explorer 和 Edge 支持规范的旧版本,而 Chrome (69+) 和 Safari 支持更新的基于元素的方法。

可以将两种方法并排使用(如果您的布局允许),以支持这两组浏览器

.container {
  scroll-snap-type: mandatory;
  scroll-snap-points-y: repeat(300px);
  scroll-snap-type: y mandatory;
}

.child {
  scroll-snap-align: start;
}

我认为,更灵活的选择是仅使用基于元素的语法,并加载 polyfill 来支持尚未支持它的浏览器。 这是我在以下示例中使用的方法。

不幸的是,polyfill 没有附带浏览器捆绑包,因此如果您没有使用构建过程,它很难使用。 我发现最简单的方法是链接到 bundle.run 上的脚本,并在 DOM 加载后使用 cssScrollSnapPolyfill() 初始化它。 值得注意的是,此 polyfill支持基于元素的语法,而不支持 repeat 方法。

父容器属性

与任何属性一样,熟悉它们接受的值是个好主意。 滚动捕捉属性应用于父元素和子元素,每个元素都有特定的值。 就像 flexbox 和 grid 一样,父元素变成 “flex” 或 “grid” 容器。 在这种情况下,父元素将变成一个捕捉容器。

以下是父容器的属性和值以及它们的工作原理。

scroll-snap-type “mandatory” 与 “proximity”

mandatory 值表示浏览器必须在用户停止滚动时捕捉到一个捕捉点。 proximity 属性限制更少,这意味着如果浏览器认为合适,它可能捕捉到一个捕捉点。 根据我的经验,当您在离捕捉点几百像素的范围内停止滚动时,这通常会生效。

在我自己的工作中,我发现 mandatory 带来更一致的用户体验,但它也可能很危险,正如 规范中所指出。 想象一个场景,其中一个滚动容器中的元素比视窗更高

如果该容器设置为 scroll-snap-type: mandatory,它将始终捕捉到元素顶部或下方元素的顶部,使得无法滚动到高元素的中间部分。

scroll-padding

默认情况下,内容将捕捉到容器的边缘。 您可以通过在容器上设置 scroll-padding 属性来更改这一点。 它遵循与常规 padding 属性相同的语法。

如果您的布局包含可能影响内容的元素,例如固定标题,这将非常有用。

子元素的属性

现在让我们继续讨论子元素的属性。

scroll-snap-align

这允许您指定元素的哪个部分应该捕捉到容器。 它有三个可能的值:startcenterend

这些是相对于滚动方向的。 如果您垂直滚动,start 指的是元素的顶边。 如果您水平滚动,它指的是左边。 centerend 遵循相同的原则。 您可以在每个滚动方向上设置一个不同的值,用空格隔开。

scroll-snap-stop “normal” 与 “always”

默认情况下,滚动捕捉仅在用户停止滚动时生效,这意味着用户可以在到达停止点之前跳过多个捕捉点。

您可以通过在任何子元素上设置 scroll-snap-stop: always 来更改这一点。 这将强制滚动容器在用户继续滚动之前停止在该元素上。

在撰写本文时,没有浏览器原生支持 scroll-snap-stop,尽管 Chrome 有一个 跟踪错误

让我们来看一些滚动捕捉的使用示例。

示例 1:垂直列表

要使垂直列表捕捉到每个列表元素,只需要几行 CSS。 首先,我们告诉容器沿着其垂直轴捕捉

.container {
  scroll-snap-type: y mandatory;
}

然后,我们定义捕捉点。 在这里,我们指定每个列表元素的顶部将是一个捕捉点

.child {
  scroll-snap-align: start;
}

示例 2:水平滑块

要制作一个水平滑块,我们告诉容器沿着其 x 轴捕捉。 我们还使用 scroll-padding 来确保子元素捕捉到容器的中心。

.container {
  scroll-snap-type: x mandatory;
  scroll-padding: 50%;
}

然后,我们告诉容器捕捉到哪些点。 要将画廊居中,我们将每个元素的中心点定义为捕捉点。

.child {
  scroll-snap-align: center;
}

示例 3:垂直全屏

我们可以直接在元素上设置捕捉点

html { /* body won't work ¯\_(ツ)_/¯ */
  scroll-snap-type: y mandatory;
}

/* Although I'm told that html doesn't work in Safari 
   and body does, so maybe use both? */

然后,我们使每个部分的大小与视窗相同,并将顶部边缘定义为捕捉点

section {
  height: 100vh;
  width: 100vw;
  scroll-snap-align: start;
}

示例 4:水平全屏

这与垂直版本的概念相同,只是捕捉点在 x 轴上。

body {
  scroll-snap-type: x mandatory;
}

section {
  height: 100vh;
  width: 100vw;
  scroll-snap-align: start;
}

示例 5:2D 图像网格

滚动捕捉可以同时在两个方向上工作。同样,我们可以在元素上直接设置 scroll-snap-type

.container {
  scroll-snap-type: both mandatory;
}

然后,我们定义每个图块的左上角为捕捉点

.tile {
  scroll-snap-align: start;
}

关于用户体验的一些想法

乱动滚动是一件风险很大的事情。因为它是与网络交互的基本组成部分,以任何方式改变它都会让人感觉不舒服——“scrolljacking” 这个词过去常常用来描述那种体验。

基于 CSS 的滚动捕捉最棒的一点是,你不会直接控制滚动位置。相反,你只是给浏览器提供一个位置列表,让它以适合平台、输入方法和用户偏好的方式进行捕捉。这意味着你构建的滚动界面会像原生界面一样(即使用相同的动画等),无论它在哪个平台上查看。

对我来说,这是 CSS 滚动捕捉相对于提供类似功能的 JavaScript 库的关键优势。

根据我的经验,这在移动设备上效果很好。也许是因为滚动捕捉已经是移动平台上原生 UI 的一部分。(想象一下 iOS 和 Android 的主屏幕——它们本质上是带有捕捉点的水平滑块。)Android 上的 Chrome 交互特别好,因为它感觉像普通滚动,但视窗总是恰好停在捕捉点。

肯定有一些复杂的数学运算才能做到这一点。多亏了 CSS 滚动捕捉,我们免费得到了它。

当然,我们不应该在所有东西上都开始设置捕捉点。像文章页面这样的东西,没有捕捉点也很好。但我认为它们在适当的情况下可以是一个不错的增强——图片库、幻灯片似乎是不错的候选对象,但也许还有其他潜在的用途。

结论

如果考虑周到,滚动捕捉可以是一个有用的设计工具。CSS 捕捉点允许你连接到浏览器的原生滚动交互,因此你的界面感觉无缝且流畅。随着 JavaScript API 可能会出现在地平线上,它们将变得更加强大。不过,轻触可能是最好的方式。