跳到主要内容位置

CSS 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>

CSS:

ul {
width: 50%;
height: 400px;
overflow: scroll;
scroll-snap-type: y mandatory;
}

li {
scroll-snap-align: start;
}

兼容性

scroll snap 的兼容性可以参考这个表格:

scroll snap

小结

好了,这个就是使用 CSS 设置滚动贴合的方式,共两步:

  • 第一步,给滚动容器设置 scroll-snap-type,第一个值为方向,可以取 x 或 y,第二个值为方式,可以取 mandatory 或 proximity。
  • 第二步,给子元素设置 scroll-snap-align,取值可以是 start、center 和 end。

我们还可以给容器设置 scroll-padding,来让贴合点与容器有一定的间距。 你学会了吗?如果有帮助请三连,想学更多有用的前端开发知识,请关注峰华前端工程师,感谢观看!

提示

《Vue 3.x 全家桶完全指南与实战》课程已上线,包括 Vue 3.x、TypeScript、Vue Router 4.x、Vuex 4.x 所有初级到高级的语法特性详解,让你完全胜任 Vue 前端开发的工作。点击查看详情。

新书《JavaScript 基础语法详解》已上架,可在京东、当当、淘宝等各大电商购买