Snap Scroll 滚动贴合特效实现
网页开发发展到现在,特效是越来越多,我们经常可以看到有的网站上边的内容,它们会在鼠标滚动的时候自动贴合到浏览器的顶部或者底部,以前实现这种特效使用的是 JavaScript,现在我们可以使用 CSS 原生属性来设置,并且使用这种方式无论在桌面端还是移动端浏览器上,都能达到操作系统级的滚动贴合效果。
设置滚动贴合
设置滚动贴合需要使用到两个属性:
- 给滚动容器设置 scroll-snap-type,滚动贴合的方向和方式。
- 给滚动的内容设置 scroll-snap-align,滚动贴合的对齐方式。
我们来看一下它们的用法,假设我们有 4 屏的内容需要进行垂直滚动贴合。我们用 main 元素表示滚动的容器,4 个 section 表示要滚动贴合的内容,为了突出重点,我们只看核心代码:
<main>
<section>页面内容1</section>
<section>页面内容2</section>
<section>页面内容3</section>
<section>页面内容4</section>
</main>
每个 section 都设置为占满全屏:
section {
width: 100vw;
height: 100vh;
}
然后把 main 元素的高度设置为 100vh,overflow 为 scroll,来把滚动条设置到 main 元素上:
main {
overflow: scroll;
height: 100vh;
}
之后是关键部分,第一步,我们给 main 元素设置 scroll-snap-type
属性:
main {
scroll-snap-type: y mandatory;
}
scroll-snap-type 需要两个值,第一个值为滚动贴合的方向,y 表示纵向滚动贴合,第二个值为贴合方式,mandatory 表示强制滚动,用户只要一滚动鼠标,下一屏内容就直接滚动上来进行贴合。 第二步,我们需要指定子元素的贴合对齐方式,使用 scroll-snap-align
属性,例如这里把它设置为 start:
section {
scroll-snap-align: start;
}
下一屏的内容会直接贴合到 main 元素顶部,这样滚动贴合就实现了。
贴合结束对齐
如果我们把 section 的高度加长,再把 scroll-snap-align 属性设置为 end,那么滚动时,下一屏内容的底部就会贴合到 main 元素的底部,例如:
section {
height: 150vh;
scroll-snap-align: end;
}
贴合居中对齐
如果把 scroll-snap-align 设置为 center,那么下一屏的内容会和 main 元素垂直居中对齐:
section {
height: 150vh;
scroll-snap-align: center;
}
水平滚动贴合
对于水平滚动贴合,它和垂直方向的设置是一样的,只需要把滚动容器中的 scroll-snap-type 中的 y 改成 x。这里我们把容器布局做一些调整,让它有水平滚动条:
main {
display: flex;
scroll-snap-type: x mandatory;
overflow: scroll;
height: 100vh;
width: 100vw;
}
section {
width: 100vw;
height: 100vh;
flex-shrink: 0;
scroll-snap-align: start;
}
这样就能实现水平方向的滚动贴合了。
使用 mandatory 强制贴合的方式适合内容尺寸较小的情况,如果有的元素内容非常长,那么在滚动的时候会直接滑动到下一屏,导致不在屏幕上的内容很快就会滑过,这个时候我们可以使用 proximity 属性,让元素滚动到离贴合点小于一定距离的时候再贴合:
main {
scroll-snap-type: y mandatory;
}
/* 非常长的内容 */
section:nth-child(3) {
height: 250vh;
}
这个距离,是浏览器自己规定的。
贴合间距
在贴合的时候,我们还可以给容器设置间距,让贴合点偏移一些距离。假如容器中有一个 sticky 粘滞的头部,占据了一定的空间:
<main>
<h1>H1标题</h1>
<section>页面内容1</section>
<section>页面内容2</section>
<section>页面内容3</section>
<section>页面内容4</section>
</main>
h1 { height: 80px; position: sticky; top: 0; }
如果直接设置滚动贴合,那么元素会贴合到 main 元素的顶部,这样会有一部分内容被粘滞头部覆盖:
main {
scroll-snap-type: y mandatory;
}
这种情况我们可以给滚动容器, 也就是 main 元素,设置 scroll-padding,大小跟粘滞头部的高度一致,这样就能在贴合的时候,与 main 元素有一定的间距,从而把头部的空间留出来:
main {
scroll-snap-type: y mandatory;
scroll-padding: 80px;
}
scroll-padding 和 padding 属性的取值方式是一样的。
非全屏贴合
滚动贴合并不仅仅可以用于全屏滚动,也可以用于 ul 这种小的列表组件中:
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
</ul>
ul {
width: 50%;
height: 400px;
overflow: scroll;
scroll-snap-type: y mandatory;
}
li {
scroll-snap-align: start;
}