使用去抖滚动事件固定元素以提高性能

Pinning Elements with Debounced Scroll Event for Performance

根据滚动位置平滑固定元素的正确方法是什么? 我尝试对滚动侦听器进行去抖动以提高性能,但固定不准确。即使将 debouncing 设置为 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 场景?

你可以试试 fixed or sticky CSS positioning:

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

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

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

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

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

你的 JS 中发生了很多事情,我认为你可能把事情搞得太复杂了,所以我会给你一些基础知识供你考虑。

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

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

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

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

这是一个简单的示例,它在滚动时将一个元素粘贴到屏幕顶部,并使用 Lodash 进行了限制。我使用的是 25 毫秒的节流阀,这大约是我建议的最大量,以保持事情看起来流畅,当您滚动超过阈值时,您不会真正注意到元素 sticking/unsticking 的延迟。您可以减少到 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>