首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >确定快速滚动元素的快速滚动事件是否完成

确定快速滚动元素的快速滚动事件是否完成
EN

Stack Overflow用户
提问于 2021-01-29 09:43:15
回答 1查看 3.7K关注 0票数 5

摘要

我正在使用一个可滚动的元素创建一个图片库。我正在使用CSS的scroll-snap功能,它允许我在滚动体中的元素(图像)上快速切换。

通过绑定到元素的scroll事件,我将在用户滚动元素(如预装、隐藏接口元素等)时应用各种操作。其中一个依赖于滚动事件,需要在滚动完成的准确时刻停止。但是,卷轴抓拍给我带来了一种不可预见的,但仍未处理的情况;

如果快速滚动操作完成,I 无法准确地确定。

我可以在每个卷轴上设置一个setTimeout,它会取消自身并重新设置--有效地取消设置--如果没有重置,最终也会被调用。但设置此值时所使用的超时,可能意味着在确定滚动完成时“为时已晚”。

底线:如何检查滚动是否完成,因为:

  1. 用户已停止滚动,或;
  2. 滚动器已经达到了它的断点(scroll-snap-type设置)
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-02-03 14:34:28

我终于彻底解决了这个脑残问题。这比我原先想的要简单得多。(注意:在我的示例中,它用于水平滚动体;在示例中将offsetWidth更改为offsetHeight,将scrollLeft更改为scrollTop,以适应垂直滚动体。)

代码语言:javascript
复制
function scrollHandler(e) {
    var atSnappingPoint = e.target.scrollLeft % e.target.offsetWidth === 0;
    var timeOut         = atSnappingPoint ? 0 : 150; //see notes

    clearTimeout(e.target.scrollTimeout); //clear previous timeout

    e.target.scrollTimeout = setTimeout(function() {
        console.log('Scrolling stopped!');
    }, timeOut);
}

myElement.addEventListener('scroll', scrollHandler);

细目

通过使用滚动元素自己的宽度(或垂直滚动的情况下的高度),我们可以通过将元素的滚动位置(在我的例子中为scrollLeft)除以其宽度(offsetWidth)来计算它是否已经达到其快照点,如果这将产生一个圆形的整数(意思是:宽度‘适合’滚动位置的精确x倍),那么它已经到达了快照点。我们通过使用余算子来做到这一点。

代码语言:javascript
复制
var atSnappingPoint = e.target.scrollLeft % e.target.offsetWidth === 0;

然后,如果到达快照点,则将timeOut (在滚动完成后应该触发的setTimeout中使用)设置为0。否则,将使用常规值(在上面的示例150中,请参见注释)。

这是因为当元素实际到达其快照点时,会触发最后一个scroll事件,而我们的处理程序也会被触发(再次)。将timeOut调整为0将立即调用 (见mdn)调用超时函数;因此,当滚动器“命中”快照点时,我们立即知道这一点。

演示

下面是一个有用的例子:

代码语言:javascript
复制
function scrollHandler(e) {
    var atSnappingPoint = e.target.scrollLeft % e.target.offsetWidth === 0;
    var timeOut         = atSnappingPoint ? 0 : 150; //see notes

    clearTimeout(e.target.scrollTimeout); //clear previous timeout

    e.target.scrollTimeout = setTimeout(function() {
        //using the timeOut to evaluate scrolling state
        if (!timeOut) {
            console.log('Scroller snapped!');
        } else {
            console.log('User stopped scrolling.');
        }
    }, timeOut);
}

myElement = document.getElementById('scroller');

myElement.addEventListener('scroll', scrollHandler);
代码语言:javascript
复制
.scroller {
  display: block;
  width: 400px;
  height: 100px;
  
  overflow-scrolling: touch;
  -webkit-overflow-scrolling: touch;
  overflow-anchor: none;
  overflow-x: scroll;
  overflow-y: hidden;

  scroll-snap-type: x mandatory;
  scroll-snap-stop: normal;
  scroll-behavior: auto;
 }
 .scroller-canvas {
  position: relative;
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
 }
 .scroller-canvas > * {
  position: relative;
  display: inline-flex;
  flex-flow: column;
  flex-basis: 100%;
  flex-shrink: 0;
  
  width: 400px;
  height: 100px;
  
  scroll-snap-align: start;
  scroll-snap-stop: normal;
 }
 .scroller-canvas > *:nth-child(even) {
  background-color: #666;
  color: #FFF;
 }

/* stackoverflow code wrapper fix */
.as-console-wrapper { max-height: 50px !important; }
代码语言:javascript
复制
<div class="scroller" id="scroller">
  <div class="scroller-canvas">
    <div class="slide" id="0">Slide 1</div>
    <div class="slide" id="1">Slide 2</div>
    <div class="slide" id="2">Slide 3</div>
    <div class="slide" id="3">Slide 4</div>
    <div class="slide" id="4">Slide 5</div>
  </div>
</div>

备注

在快照点上滚动--我还没有实现/考虑到这样一个事实:您可以通过滚动它来“击中”快照点。不过,这是非常罕见的。当我找到解决方案时,我会更新答案。

像素比:如果您得到了错误的计算(1 px的剩余部分,因此函数没有正确解决),您可能会遇到一些缩放/框模型问题,这些问题会使滚动位置的计算和offsetWidth的计算都搞砸。这是不可能的,这是由设备的像素比,所以你可以尝试‘纠正’的值(滚动左和宽度)乘以这些像素比。重要的是:像素比不仅表示用户是否有高dpi屏幕,而且当用户缩放页面时,它也会发生变化。

当滚动尚未达到断点时使用的超时值为150。这足以防止它在Safari @ iOS完成滚动之前被触发(它使用bezier曲线来滚动抓取,产生一个非常长的“最后一帧”约为120-130 is ),足够短到当用户暂停在快照点之间滚动时产生可接受的结果。

scroll-padding如果在滚动元素上设置了scroll-padding,则在确定快照点时需要考虑到这一点。

像素剩余:您甚至可以进一步细分,在到达快照点之前计算出剩余的像素:

代码语言:javascript
复制
var pxRemain = e.target.scrollLeft % e.target.offsetWidth;
var atSnappingPoint = pxRemain === 0;

但是请注意,您需要从元素的宽度中减去这个值,这取决于滚动的方式。这需要计算滚动的距离,并检查是负的还是正的。然后,它将变成:

代码语言:javascript
复制
var pxRemain = e.target.scrollLeft % e.target.offsetWidth;
    pxRemain = (pxRemain === 0) ? 0 : ((distance > 0) ? pxRemain : elementWidth - pxRemain);
var atSnappingPoint = pxRemain === 0;

只抓拍

编写此脚本时考虑到了两种情况:

  1. 这个元素已经断裂到了它的断点,或者;
  2. 用户已经停止滚动(或者抓取检测出了问题)。

如果只需要前者,则不需要超时,只需编写:

代码语言:javascript
复制
function scrollHandler(e) {
  if (e.target.scrollLeft % e.target.offsetWidth === 0) {
    console.log('Scrolling is done!');
  }
}

myElement.addEventListener('scroll', scrollHandler);
票数 13
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65952068

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档