确定快照滚动元素的快照滚动事件是否完成 [英] Determine if a snap-scroll element's snap scrolling event is complete

查看:93
本文介绍了确定快照滚动元素的快照滚动事件是否完成的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am creating an image gallery using a scrollable element. I am using CSS' scroll-snap feature, which allows me to snap onto the elements (images) in the scroller.

通过绑定到元素的 scroll 事件,当用户滚动元素时,我将应用各种操作(例如预加载,隐藏界面元素等).其中之一取决于滚动事件,并且需要在确切时刻停止滚动.但是滚动捕捉使我遇到了无法预料但尚未处理的情况;

By binding to the element's scroll event, I am applying various actions when the user is scrolling the element (things like preloading, hiding interface elements, etc). One of these is dependent on the scrolling event and needs to stop at the exact moment scrolling is completed. But scroll-snapping presents me with an unforeseen, and yet un-handled, situation;

我无法准确确定快速滚动动作是否完成.

I can't accurately determine if the snap-scrolling action is complete.

我可以在每个滚动上设置一个 setTimeout ,它会自动取消并重置-有效地反跳-如果没有重置,最终会被调用.但是,设置此项时使用的超时时间可能意味着确定滚动完成时太迟了".

I can set a setTimeout on each scroll, which cancels itself and re-sets - effectively debouncing - and finally does get called if not reset. But the timeout used when setting this, can mean you are 'too late' when determining scrolling is done.

底线:由于以下原因,如何检查滚动是否完成:

Bottom line: how do I check if scrolling is done, either because:

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

推荐答案

我终于确定地解决了这个难题.解决起来比我最初想的要简单得多.(请注意:在我的情况下,它是用于水平滚动条的;在菜单中将 offsetWidth 更改为 offsetHeight ,将 scrollLeft 更改为 scrollTop 适用于垂直滚动条的示例.)

I have finally, definitively, solved this brainteaser. It was much more simple to solve than I originally thought. (Note: in my case it's for a horizontal scroller; change offsetWidth to offsetHeight and scrollLeft to scrollTop in the example to adapt for a vertical scroller.)

function scrollHandler(e) {
    var atSnappingPoint = e.target.scrollLeft % e.target.offsetWidth === 0;
    var timeOut         = atSnappingPoint ? 0 : 150; //see notes

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

myElement.addEventListener('scroll', scrollHandler);

故障

通过使用滚动元素自己的 width (或垂直滚动​​,如果是高度),我们可以通过除以元素的滚动位置( scrollLeft 在我的情况下)乘以其宽度( offsetWidth ),如果生成的是舍入整数(表示:宽度恰好"适合滚动位置 x倍)已达到捕捉点.我们通过使用余数运算符来完成此操作:

Breakdown

By using the scrolling element's own width (or height in case of vertical scroll) we can calculate if it has reached its snapping point by dividing the element's scrollposition (scrollLeft in my case) by its width (offsetWidth), and if that produces a round integer (meaning: the width 'fits' the scrolling position exactly x times) it has reached the snapping point. We do this by using the remainder operator:

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

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

Then, if snapping point is reached, you set the timeOut (used in the setTimeout that should fire when scrolling has finished) to 0. Otherwise, the regular value is used (in the above example 150, see notes).

之所以起作用,是因为当元素实际到达其捕捉点时,将触发最后一个 scroll 事件,并且将再次触发我们的处理程序.然后将 timeOut 调整为0,即可立即(

This works because when the element actually reaches its snapping point, one last scroll event is fired, and our handler is fired (again). Adjusting the timeOut to 0 will then instantly (see mdn) call our timeout function; so when the scroller 'hits' the snapping point, we know that instantaneously.

在捕捉点上滚动我尚未实现/考虑过以下事实:您可以通过在捕捉点上滚动来击中"捕捉点.但是,这种情况极为罕见.找到解决方案后,我将更新答案.

scrolling over snapping point I have not yet implemented/taken into account the fact that you can 'hit' the snapping point by scrolling over it. This is extremely rare, though. I will update the answer when I find a solution.

像素比率:如果您搞砸了计算(剩余1 px等,因此该功能无法正确解析),则可能存在一些缩放/框模型问题,这些问题困扰了滚动位置和offsetWidth计算的计算.这是由设备的像素比率引起的,因此可能会出现偏差,因此您可以尝试通过将它们乘以像素比率来校正"值(向左滚动和向左滚动).重要提示:像素比不仅可以指示用户是否具有高dpi的屏幕,而且还可以在用户缩放页面时进行更改.

pixel ratio: If you get messed up calculations (remainder of 1 px, etc, so the function is not resolving correctly), you probably have some scaling/box-model issues that mess up the calculation of both the scrolling position and the offsetWidth calculation. There is an off-chance this is caused by the device's pixel ratio, so you can try to 'correct' the values (scrollleft and width) by multiplying these by the pixelratio. Important: turns out pixel ratio does not only indicate if the user has a high-dpi screen, but it also changes when the user has zoomed the page.

超时滚动尚未达到捕捉点时使用的树状记录 timeOut 为150.此长度足以防止在Safari @ iOS完成滚动之前被触发(它使用贝塞尔曲线进行滚动捕捉,这会产生一个非常长的最后一帧",大约120-130ms),并且足够短,以便当用户在捕捉点之间暂停滚动时产生可接受的结果.

timeout the arbirtrary timeOut used when scrolling has not yet reached snapping point is at 150. This is long enough to prevent it being fired before Safari @ iOS is done scrolling (it uses a bezier curve for scroll snapping, which produces a very long 'last frame' of around 120-130ms) and short enough to produce an acceptible result when the user pauses scrolling in between snapping points.

滚动填充(如果已在滚动元素上设置了 scroll-padding ),则在确定捕捉点时需要考虑到这一点.

scroll-padding if you have set scroll-padding on the scroll element, you will need to take that into account when determining the snapping point.

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

pixels remaining: You could even break things down further, to calculate the pixels remaining before reaching snapping point:

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

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

But note that you will need to subtract that from the element's width, depending on which way you are scrolling. This requires you to calculate the distance scrolled, and checking if that is negative or positive. Then it would become:

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

仅捕捉

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

Only snapping

This script is written so it takes into account two situations:

  1. 该元素已捕捉到其捕捉点,或者;
  2. 用户已暂停滚动(或快照检测出了点问题)

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

If you only need the former, you don't need the timeout, and you can just write:

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

myElement.addEventListener('scroll', scrollHandler);

这篇关于确定快照滚动元素的快照滚动事件是否完成的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆