实战CSS Scroll Snapping
特别声明,本文根据@MAX KOHLER的《Practical CSS Scroll Snapping》一文所整理。
CSS Scroll Snap(CSS 滚动捕捉)允许你在用户完成滚动后多锁定特定的元素或位置。它非常适合构建像下面这样的交互:
浏览器支持和基本用法
自2016年推出CSS Scroll Snap以来,浏览器对它的支持有了显著的改善。Google 69+、Firefox、Edge和Safari都支持它的某些版本。
通过在容器元素上设置scroll-snap-type
属性并在其子元素上设置scroll-snap-align
属性来使用滚动捕捉。当滚动容器元素时,它将捕捉到你定义的子元素。最基本的结构像下面这样:
<!-- HTML -->
<div class='container'>
<section class='child'></section>
<section class='child'></section>
<section class='child'></section>
...
</div>
// CSS
.container {
scroll-snap-type: y mandatory;
}
.child {
scroll-snap-align: start;
}
这与第一个版本的规范有所不同,后者允许你使用repeat
关键词手动设置捕捉点:
.container {
scroll-snap-points-y: repeat(300px);
}
这种方法非常有限。由于它只允许均匀间隔的捕捉点,因此你无法真正构建捕捉到不同大小元素的接口。如果元素在不同的屏幕尺寸之间改变形状,你也必然会遇到问题。
在撰写本文时,Firefox、IE和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没有提供浏览器包,如果你的项目中没有使用构建工程,那么使用这个Polyfill就会有点棘手。我找到一个最简单的方法是链接到
bundle.run
上的脚本,并在加载DOM之后,使用cssScrollSnapPolyfill()
初始化它。值得指出的是,这个Polyfill仅支持基于元素的语法,而不支持repeat
语法。
父容器属性
与任何属性一样,熟悉它们所接受的值是一个好主意。滚动捕捉属性应用于父元素和子元素,每个元素都有特定的值。就像Flexbox和Grid那样,父类变成了Flex或Grid容器。在这种情况下,父元素变成了快照(Snap)容器。
下面是父容器属性和值以及它们的工作方式。
scroll-snap-type: mandatory vs. 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 vs. always
默认情况下,滚动捕捉仅在用户停止滚动时启动,这意味着用户可以在停止之前跳过多个捕捉点。
你可以在任何子元素上设置scroll-snap-stop: always
来改变它。这会强制滚动容器在该元素上停止,然后用户可以继续滚动。
在写这篇文章的时候,没有一个浏览器支持scroll-snap-stop
。
接下来让我们来看一些有关于CSS Scroll Snap的示例。
案例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:垂直全屏
我们可以直接在<body>
元素y
轴上设置捕捉点:
body {
scroll-snap-type: y mandatory;
}
然后,我们将每个部分的大小设置和视窗一样大,并将顶部边缘定义为捕捉点:
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图像网格
滚动捕捉可以同时在两个方向工作。同样,我们可以直接在<body>
元素上设置scroll-snap-type
:
.container {
scroll-snap-type: both mandatory;
}
然后,将每个.tile
左上角定义为一个捕捉点:
.tile {
scroll-snap-align: start;
}
关于用户体验的一些想法
摆弄滚动条是有风险的。由于它是与Web交互的一个基本部分,以任何方式更改它都会让一感觉不协调 —— 滚动劫持(Scrolljacking)这个术语就用来描述这种体验。
基于CSS Scroll Snap的好处在于,你不能直接控制滚动的位置。相反,你只是向浏览器提供一个位置列表,以适合平台、输入法和用户首选项的方式进行捕捉。这意味着你构建的滚动界面将与本机界面一样。
对我来说,这是CSS Scroll Snap在提供类似JavaScript库的一些类似功能特性。
以我的经验来看,这很有效,尤其在移动设备上。也许这是因为滚动捕捉已经是移动平台上的本机UI的一部分。(想象一下iOS和Android上的主屏幕 —— 它们本质上是带捕捉点的滑动块。)Android的Chrome上的交互特别好,因为它感觉像是常规滚动,但是视口总是在一个瞬间停下来:
肯定有一些奇特的数学方法实现这一切。多亏了 CSS Scroll Snap,我们不需要去探究那些复杂的数学方法。
当然,我们不应该开始对所有的事情都投扣分。像文章里面这样的东西没有它们也可以。但我认为在适当的情况下,它们可以是一个很好的增强 —— 图片库,幻灯片似乎是很好的选择,但可能还有更多的潜力。
CSS还提供了一些其他的属性可以用来帮助我们改善用户体验,如果感兴趣,可以阅读《滚动的特性》和《改变用户体验的滚动新特性》。
总结
如果考虑周全,滚动捕捉可以是一个有用的设计工具。CSS Scroll Snap Points允许你连接到浏览器的本机滚动交互,因此你的界面感觉无缝且平滑。随着JavaScript API的出现,这些功能将变得更加强大。不过,触摸(touch
)或许是正确的做法。
扩展阅读
- 提前思考CSS Scroll Snap Points
- CSS Scroll Snap Points简介
- CSS Scroll Snap Points规范
- Well-Controlled Scrolling with CSS Scroll Snap
- CSS Scroll Snap: What Is It? Do We Need It?
- Intuitive Scrolling Interfaces with CSS Scroll Snap Points
- Swipe Views with CSS Snap Points: Building a More Efficient Mobile Web Navigation
- Get up to speed with scroll snap pointsnike air max 90 zappos