【问题标题】:Pinning Elements with Debounced Scroll Event for Performance使用 Debounced Scroll 事件固定元素以提高性能
【发布时间】:2019-08-24 23:47:11
【问题描述】:

根据滚动位置平滑固定元素的正确方法是什么? 我尝试对滚动侦听器进行去抖动以提高性能,但固定不准确。即使将去抖动设置为 10 毫秒,它也不平滑,并且元素也不会干净地捕捉到其初始位置。

var scrolling = false;
var stickPosY = 100;
var heights = [];

$(".element").each( function(index) {
  heights[index] = $(".element[data-trigger=" + index + "]").offset().top;
});

function pin() {
  if ( !$("#aside").hasClass("fixed") ) {

    var stickyLeft = $("#aside").offset().left;
    var stickyWidth = $("#aside").outerWidth();
    var stickyTop = $("#aside").offset().top - stickPosY;

    $("#aside").addClass("fixed"); 
    $("#aside").css({"left": stickyLeft, "top": stickyTop, "width": stickyWidth});
  }
}

function unpin() {
  $("#aside").css({"left": "", "top": "", "width": ""});
  $("#aside").removeClass("fixed")
}

$( window ).scroll( function() {
  scrolling = true;
});

setInterval( function() {
  if ( scrolling ) {
    scrolling = false;

    var y = window.scrollY;
    console.log(y);

    // PIN SIDEBAR
    y > stickPosY ? pin() : unpin();

    //TRIGGERS
    for (var i=0; i < heights.length; i++) {  
      if (y >= heights[i]) {
        $('.element[data-trigger="' + i + '"]').addClass("blue");
      }
      else {
        $('.element[data-trigger="' + i + '"]').removeClass("blue");
      }
    }
  }
}, 250 );

这是我的Pen

我尝试在带有大头针和其他触发器的场景中使用 scrollMagic 进行项目,但滚动不是很流畅。所以我试图用一个精简的版本和去抖动的监听器来重建它。这种方法是否可行,还是我应该尝试优化我的 scrollMagic 场景?

【问题讨论】:

    标签: javascript jquery performance scroll debounce


    【解决方案1】:

    正如 James 指出的那样,您可以只使用 position: sticky 作为一种选择,但这在旧浏览器中不起作用,并且它的使用仅限于较新浏览器中更简单的情况,所以我将继续使用 JS 解决方案,假设你想走那条路。

    你的 JS 中发生了很多事情,我认为你可能过于复杂了,所以我会给你一些基本的考虑。

    1. 当您基于滚动切换事物时,可以切换内联样式或类,但不能同时切换两者。我建议切换一个类,因为它允许您拥有一个可以在多种屏幕尺寸上工作的功能(即,您可以使用媒体查询来根据屏幕尺寸更改切换类的行为)。此外,它将所有样式保存在一个位置,而不是让它们在 JS 和样式表之间分开。

    2. 尽量减少滚动时的工作量。例如,缓存对滚动函数外部变量中元素的引用,这样您就不会在每次滚动像素时都不断地查找它们。避免在滚动函数中出现循环。

    3. 通常不推荐使用setInterval 来提高滚动功能的性能。要做的就是每隔 X 时间运行一个函数,一直运行一个函数,无论你是否滚动。你真正想做的是直接限制你的滚动功能。这样,如果您真正快速滚动很长一段距离,您的函数只会被调用总次数的一小部分,但如果您缓慢滚动一小段距离,它仍然会被调用最少次数以保持内容看起来很流畅,如果你根本不滚动,那么你根本就没有调用你的函数。此外,在这种情况下,您可能希望限制您的函数,而不是去抖动它。

    4. 考虑使用 Underscore.js 或 Lodash.js 中的节流函数,而不是自己发明,因为这些函数具有高性能并且保证可以在各种浏览器中工作。

    这是一个简单的例子,在滚动时将一个元素粘贴到屏幕顶部,并使用 Lodash 进行节流。我正在使用 25 毫秒的油门,这大约是我建议的最大量,可以让您在滚动超过阈值时不会真正注意到元素粘着/不粘着的延迟。您可以缩短到 10 毫秒。

    $(function() {
      $(window).on('scroll', _.throttle(toggleClass, 25));
    
      const myThing = $('#my-thing');
      const threshold = $('#dummy-1').height();
    
      function toggleClass() {
        const y = window.scrollY;
        if (y > threshold) {
          myThing.addClass('stuck')
        } else {
          myThing.removeClass('stuck');
        }
      }
    });
    #dummy-1 {
      height: 150px;
      background-color: steelblue;
    }
    #dummy-2 {
      height: 150px;
      background-color: gold;
    }
    #my-thing {
      width: 300px;
      height: 75px;
      background-color: firebrick;
      position: absolute;
      top: 150px;
      left: 0;
    }
    #my-thing.stuck {
      position: fixed;
      top: 0;
    }
    body {
      margin: 0;
      height: 2000px;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.0.0/lodash.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    
    <div id="dummy-1"></div>
    <div id="dummy-2"></div>
    <div id="my-thing"></div>

    【讨论】:

    • 感谢您的详细解答。注意到即使油门设置为 25 毫秒,快速滚动也会有微小的跳跃。真的很好奇应该如何做到这一点,限制滚动事件似乎是必要的,但对于固定它并没有真正起作用,因为它不精确。我觉得最好的办法是减少触发器范围内的节流,但这听起来又过于复杂了。 js 中发生这么多事情的原因是我试图让这个 jank 免费demo.floriangrohmann.de/en/services
    • @flobb 以我的经验,快速滚动的小亮点基本上是不可避免的。您可以将油门降低到 10 毫秒,但最终总会在外观与性能之间进行权衡;这只是在这里做生意的成本。实际上,用户甚至不太可能注意到。您可以在此演示中看到的微小亮点与以往一样糟糕,因为我特意使用了明亮、对比鲜明的颜色,它们完美地排列在一起来展示效果。但是,在您的演示站点中,并没有这么清晰的界限,因此人们可能甚至不会注意到问题。
    • 顺便说一句,我要给你的提高转换性能的一个提示是避免使用 all 关键字,而只转换你真正想要的。我没有在您的演示站点上深入研究您的代码,但这只是我立即注意到的一件事。如果你想在同一个元素上转换多个属性,你可以用逗号分隔转换,例如transition: height 300ms ease, opacity 300ms ease
    • 听起来不错,可以试试。如果我为固定设置一个几乎没有节流的侦听器,并为添加过渡类设置第二个侦听器,这会有所不同吗?或者我只是在一个扩展的监听器上添加一个不太扩展的监听器?顺便说一句,有这类东西的入门课程吗? :-))
    • 非常感谢,我正在体验您的提示的第一次改进。希望我能阅读更多有关它的信息
    【解决方案2】:

    你可以试试fixed or sticky CSS positioning:

    #element {
      position: fixed;
      top: 80px;
      left: 10px;
    }
    

    Position: fixed 将使元素始终保持在距顶部 80px 和距左边缘 10px 的位置,无论滚动位置如何。

      #element{
        position: sticky;
        top: 0;
        right: 0;
        left: 0;
      }
    

    这是我的一个项目。该元素是一个导航栏。它位于标题栏下方,因此当您位于页面顶部时,您会看到标题,然后是其下方的导航,当您向下滚动时,标题会移出屏幕,但导航会停留在顶部并且始终可见.

    【讨论】:

    • 它使用的是postition:fixed,但是如果你滚动的速度不是很慢的话,去抖动会导致跳转。没有去抖动就很好,但性能很棘手。在更复杂的网站上 /w 视频 + 滚动时的附加触发器会卡顿 :(
    猜你喜欢
    • 1970-01-01
    • 2011-07-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-19
    • 2012-04-01
    • 2017-05-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多