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

浏览器支持和基本用法
自从 CSS 滚动捕捉在 2016 年推出以来,其浏览器支持已大幅提升,Google Chrome (69+)、Firefox、Edge 和 Safari 都支持某种版本的滚动捕捉。
这些浏览器支持数据来自 Caniuse,其中包含更多详细信息。 数字表示浏览器从该版本开始支持该功能。
桌面
Chrome | Firefox | IE | Edge | Safari |
---|---|---|---|---|
69 | 68 | 11* | 79 | 11 |
移动设备 / 平板电脑
Android Chrome | Android Firefox | Android | iOS Safari |
---|---|---|---|
127 | 127 | 127 | 11.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
这允许您指定元素的哪个部分应该捕捉到容器。 它有三个可能的值:start
、center
和 end
。

这些是相对于滚动方向的。 如果您垂直滚动,start
指的是元素的顶边。 如果您水平滚动,它指的是左边。 center
和 end
遵循相同的原则。 您可以在每个滚动方向上设置一个不同的值,用空格隔开。
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 可能会出现在地平线上,它们将变得更加强大。不过,轻触可能是最好的方式。
移动设备上的用户体验仍然很糟糕。使用上面的演示进行操作时,出现了很多意外行为,例如无法滚动到某些元素的后面以及跳到看似随机的位置。在它有效的情况下,它确实很酷。
演示在 Safari iOS 11.3 中不起作用。
WebKit 前缀应该可以解决这个问题。
@Brant 它们没有。Safari 11 应该支持它,没有前缀。旧版本使用前缀。
看起来 Safari 似乎支持滚动捕捉,但实际上它并不支持。我真的无法让演示正常工作,甚至无法让一个未填充的演示正常工作。
在 Chrome 或桌面 Safari 中没有问题。
自从相当多的版本发布后,Opera 也支持它:-)
感谢你写这篇文章。随着 Chrome 最终开始支持,我们现在可以开始使用它了。
我一直希望有人能写一篇关于如何实现跨浏览器滚动捕捉支持的博文,现在它出现了。
在移动设备上可能感觉不同,但在我的笔记本电脑上试用演示让我很生气。它一点也不自然,我很难导航到我想要的位置。如果我在一个实际的网站上有这样的体验,我再也不会访问那个网站了。所以我想你的警告很重要。如果你要乱动人们的滚动,你应该对自己正在做的事情 100% 确定。不过这篇文章很有趣。
这项技术非常有趣,并且如果应用得当,可以对用户有所帮助。
但到目前为止,这种 scroll-jacking 是一种噩梦般的可用性问题。你可以应用它,但永远不要取消用户的控制。
支持还不够好。你的图片显示 Chrome 69 支持,但截至 2018 年 8 月,v 69 的使用率仅为 0.04%。Chrome 67 在全球的使用率为 23%,但没有滚动捕捉支持
我在 Firefox 60.2.0esr 上的 Codepen 上试用了“示例 4:水平全屏”,它运行良好,但当我将所有代码粘贴到 html 文件中时,它就不再工作了。你还需要什么?