【问题标题】:Determine if a snap-scroll element's snap scrolling event is complete确定 snap-scroll 元素的 snap 滚动事件是否完成
【发布时间】:2021-05-03 05:32:35
【问题描述】:

摘要

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

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

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

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

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

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

【问题讨论】:

    标签: javascript css animation easing scroll-snap


    【解决方案1】:

    我终于彻底解决了这个脑筋急转弯。解决起来比我最初想象的要简单得多。 (注意:在我的情况下,它用于水平滚动条;在示例中将 offsetWidth 更改为 offsetHeightscrollLeftscrollTop 以适应垂直滚动条。)

    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);
    

    故障

    通过使用滚动元素自己的宽度(或垂直滚动​​的情况下的高度),我们可以通过将元素的滚动位置(在我的情况下为scrollLeft)除以计算它是否已达到其捕捉点它的宽度 (offsetWidth),如果它产生 一个整数(意思是:宽度“适合”滚动位置 正好 x 次)它已经到达捕捉点.我们使用remainder operator

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

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

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


    注意事项

    滚动捕捉点 我还没有实现/考虑您可以通过滚动来“命中”捕捉点这一事实。不过,这种情况极为罕见。当我找到解决方案时,我会更新答案。

    像素比:如果你搞砸了计算(剩余的 1 px 等,所以函数不能正确解析),你可能有一些缩放/盒子模型问题搞砸了滚动位置的计算和 offsetWidth 的计算。 这是由设备的像素比率引起的,因此您可以尝试通过将这些值乘以像素比率来“纠正”这些值(向左滚动和宽度)。 重要提示:事实证明,像素比率不仅表明用户是否拥有高 dpi 屏幕,而且在用户缩放页面时也会发生变化

    超时 滚动尚未达到捕捉点时使用的任意 timeOut 为 150。这足以防止在 Safari @ iOS 完成滚动之前触发它(它使用贝塞尔曲线用于滚动捕捉的曲线,它会产生大约 120-130 毫秒的非常长的“最后一帧”)并且足够短以在用户在捕捉点之间暂停滚动时产生可接受的结果。

    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);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-04-15
      • 1970-01-01
      • 2021-02-11
      • 1970-01-01
      • 2021-06-25
      • 2019-10-14
      • 2015-04-23
      相关资源
      最近更新 更多