繁体   English   中英

确定 snap-scroll 元素的 snap scrolling 事件是否完成

[英]Determine if a snap-scroll element's snap scrolling event is complete

抽象的

我正在使用可滚动元素创建图片库。 我正在使用 CSS 的scroll-snap功能,它允许我捕捉到滚动条中的元素(图像)。

通过绑定到元素的scroll事件,我在用户滚动元素时应用各种操作(如预加载、隐藏界面元素等)。 其中之一取决于滚动事件,需要在滚动完成的确切时刻停止。 但是滚动快照给我带来了无法预料但尚未处理的情况;

我无法准确确定快照滚动操作是否完成。

我可以在每个滚动条上设置一个setTimeout ,它会自行取消并重新设置 - 有效地消除抖动 - 如果不重置,最终会被调用。 但是设置它时使用的超时可能意味着您在确定滚动完成时“为时已晚”。

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

  1. 用户已停止滚动,或者;
  2. 滚动条已到达其吸附点(设置了scroll-snap-type

我终于明确地解决了这个脑筋急转弯。 解决起来比我原先想象的要简单得多。 (注意:在我的例子中,它用于水平滚动条;在示例offsetWidth更改为offsetHeight并将scrollLeft更改为scrollTop以适应垂直滚动条。)

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 )来计算它是否已经到达它的吸附点,如果这产生了一个圆形integer (意思是:宽度“适合”滚动 position正好 x 次)它已经到达捕捉点。 我们通过使用余数运算符来做到这一点:

var atSnappingPoint = e.target.scrollLeft % e.target.offsetWidth === 0;

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

这是可行的,因为当元素实际到达其吸附点时,将触发最后一个scroll事件,并且(再次)触发我们的处理程序。 timeOut调整为 0 将立即参见 mdn )调用我们的超时 function; 所以当滚动条“击中”捕捉点时,我们会立即知道。

演示

下面是一个工作示例:

 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);
 .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; }
 <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 的余数等,所以 function 没有正确解析),你可能有一些缩放/盒模型问题搞乱了滚动 position 和 offsetWidth 计算的计算. 这有可能是由设备的像素比引起的,因此您可以尝试通过将这些值乘以像素比来“更正”这些值(向左滚动和宽度)。 重要提示:事实证明,像素比率不仅表明用户是否拥有高 dpi 屏幕,而且在用户缩放页面时也会发生变化

超时滚动尚未达到捕捉点时使用的任意超时为 150。这足够长以防止它在timeOut @ iOS 完成滚动之前被触发(它使用贝塞尔曲线进行滚动捕捉,这会产生很长的'last frame' 大约 120-130ms)并且当用户在捕捉点之间暂停滚动时足够短以产生可接受的结果。

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

剩余像素:您甚至可以进一步分解,以计算到达捕捉点之前剩余的像素:

var pxRemain = e.target.scrollLeft % e.target.offsetWidth;
var atSnappingPoint = pxRemain === 0;

但请注意,您需要从元素的宽度中减去它,具体取决于您滚动的方式。 这需要您计算滚动的距离,并检查它是负数还是正数。 那么它会变成:

var pxRemain = e.target.scrollLeft % e.target.offsetWidth;
    pxRemain = (pxRemain === 0) ? 0 : ((distance > 0) ? pxRemain : elementWidth - pxRemain);
var atSnappingPoint = pxRemain === 0;

只拍

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

  1. 元素已经捕捉到它的捕捉点,或者;
  2. 用户已暂停滚动(或捕捉检测以某种方式出错)

如果你只需要前者,你不需要超时,你可以这样写:

function scrollHandler(e) {
  if (e.target.scrollLeft % e.target.offsetWidth === 0) {
    console.log('Scrolling is done!');
  }
}

myElement.addEventListener('scroll', scrollHandler);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM