灏天阁

Snap Scroll 滚动贴合特效实现

· Yin灏

网页开发发展到现在,特效是越来越多,我们经常可以看到有的网站上边的内容,它们会在鼠标滚动的时候自动贴合到浏览器的顶部或者底部,以前实现这种特效使用的是 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;
}

- Book Lists -