弹性溢出滚动

Avatar of Dave Seidman
Dave Seidman

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 $200 免费积分!

一位客户询问我们是否可以模仿许多移动设备上的 “橡皮筋”滚动行为。我相信你知道我在说什么。这是一种已经存在并且在大多数浏览器中自动发生的。例如,在 iOS Safari 中,您可以将页面滚动到视窗的顶部或底部边缘以外几百像素,然后松开会将页面弹回原位。

我听说过一些情况下,有人 可能想要阻止反弹发生,但没有人要求我实现它,尤其是在支持没有触控界面的设备的方式。实际上,我很惊讶没有现有的 CSS 属性来实现这个功能。虽然有非标准的 -webkit-overflow-scrolling 属性,但它用于不同类型的 “动量”滚动。我也不会想依赖一个尚未纳入规范的非标准属性。

好的,那么如果我们想在工作中强制执行这种橡皮筋效果呢?首先,我们需要某种元素作为需要滚动的内容的容器。当然,我们可以使用 JavaScript,但这涉及添加滚动监听器或 pointerDownpointerUppointerMove 事件的组合,更不用说跟踪位置、惯性运动等等了。

一个仅使用 CSS 的解决方案会更加理想。

这里有一个包含几个子元素的容器

<div class="carousel">
  <div class="slides">
    <div class="slide">1</div>
    <div class="slide">2</div>
    <div class="slide">3</div>
    <div class="slide">4</div>
    <div class="slide">5</div>
  </div>
</div>

让我们设置一些基本样式,具体来说是创建一种我们能够保证溢出父容器的情况。

/* Parent container with fixed dimensions for overflow */
.carousel {
  width: 200px;
  height: 400px;
  overflow-x: hidden;
  overflow-y: auto;
}

/* Wrapper for slides, stacked in a column */
.slides {
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
  width: 100%;
  height: fit-content;
}

/* Each slide is the full width of the carousel */
.slide {
  width: 100%;
  aspect-ratio: 1;
}

让我们先添加一些垂直边距。如果你的容器只有一个很长的项目,请将其添加到子元素的顶部和底部。如果容器包含多个子元素,你可能需要将 margin 添加到第一个子元素的顶部和最后一个子元素的底部。

.carousel > .slides > .slide:first-child {
  margin-top: 100px;
}

.carousel > .slides > .slide:last-child {
  margin-bottom: 100px;
}

太好了!现在我们可以滚动到边缘以外,但我们需要一些东西在用户松开手指或指针后将其弹回。为此,我们需要使用 scroll-snap-typescroll-snap-align 属性

.carousel {
  scroll-snap-type: y mandatory;
}

.carousel > .slides > .slide {
  scroll-snap-align: start;
}

.carousel > .slides > .slide:first-child {
  margin-top: 100px;
}

.carousel > .slides > .slide:last-child {
  scroll-snap-align: end;
  margin-bottom: 100px;
}

请注意,同样适用于水平滚动的元素。为此,你需要将 margin 应用于元素的左右边缘而不是上下边缘。同时,你也需要将 scroll-snap-type 属性的值从 y mandatory 更改为 x mandatory

就是这些了!这是最终的演示

我知道,我知道。这并不是什么惊天动地的效果,但这确实解决了一个非常特定的问题。如果你发现自己遇到了这种情况,现在你手里就有可以用的东西了。

其他资源