滚动性能问题的更新页面哈希

时间:2016-05-17 19:05:39

标签: jquery performance

每次用户滚动到某个部分时,我都会尝试更新页面哈希:

<section data-anchor="projects"></section>

$(document).bind('scroll',function(e){
    $('section').each(function(){
      var element = $(this),
      dataHashValue = element.data('anchor');

      if(dataHashValue) {
        var sectionOffset = 10,
        elementOffsetTop = element.offset().top,
        elementHeight = element.height(),
        currentPageOffsetY = window.pageYOffset;

        if (elementOffsetTop < currentPageOffsetY + sectionOffset
          && elementOffsetTop + elementHeight > currentPageOffsetY + sectionOffset) {
          window.location.hash = dataHashValue;
        }
      }
    });
  });

我的问题是我遇到了性能问题,当用户快速滚动时,帧速率会下降很多......如何优化此代码? (我尝试了历史API,但这让事情变得更糟)。

编辑:按照Taytorious的建议,我实现了一个Debouncer,但更新功能中的循环仍在大幅降低我的帧率

function AutoHash() {
    this.scroller = window;
    this.elements = $('[data-anchor]').map(function(index, item) {
      var $element = $(item);

      return {
        yOffset: $element.offset().top,
        height: $element.height(),
        dataHashValue: $element.data('anchor')
      };
    });
  }

update : function (lastScroll, currentScroll) {
      this.elements.each(function(index, item) {
          var sectionOffset = 10,
          currentPageOffsetY = currentScroll;

          if (item.yOffset < currentPageOffsetY + sectionOffset
            && item.yOffset + item.height > currentPageOffsetY + sectionOffset) {
            window.location.hash = item.dataHashValue;
          }
      });
    }

2 个答案:

答案 0 :(得分:1)

您可以使用这样的智能滚动:)

var smartScroll;

$(window).scroll(function(){
    clearTimeout(smartScroll);
    smartScroll = setTimeout(doneScrolling, 100);
});

function doneScrolling(){
    console.log('Your performance scrollcode here');
}

它的作用是在滚动停止后运行100ms。您甚至可以将ms降低到您认为适合性能的范围。也许20ms也会。

更准确地说,如果用户继续在ms限制内滚动,它会终止donescrolling功能。但是当用户停止滚动时,它不会终止完成滚动功能的最后一次设置超时调用。

和平与爱!

答案 1 :(得分:1)

问题是浏览器每秒多次触发滚动事件(因浏览器而异)。您正在迭代循环并操纵DOM,这些是快速连续执行的昂贵操作。你可以通过滚动debouncer和一点代码重构来解决这个问题。对于滚动去抖动:

function scrollDebounce(context, callback) {
    var latestKnownScrollY = 0,
        lastScroll = 0;
    ticking = false;

    function onScroll() {
        latestKnownScrollY = window.scrollY;
        requestTick();
    }

    function requestTick() {
        if(!ticking) {
            requestAnimationFrame(update);
        }
        ticking = true;
    }

    function update() {
        // reset the tick so we can
        // capture the next onScroll
        ticking = false;


        var currentScrollY = latestKnownScrollY;

        callback.apply(context, [lastScroll, currentScrollY]);

        lastScroll = currentScrollY;
    }


    $(window).scroll(onScroll);
}

仅当浏览器请求新帧时才会执行回调。对于回调,您可能不希望每次都遍历所有部分。我将所有部分的偏移位置存储在某种数据结构中,并跟踪当前部分。这样,您可以轻松地迭代到下一个/上一个部分,具体取决于用户是向上还是向下滚动。

this.currentIndex = 0;

function AutoHash() {
    this.scroller = window;
    this.elements = $('[data-anchor]').map(function(index, item) {
        var $element = $(item);

        return {
            top: $element.offset().top,
            bottom: $element.offset().top + $element.height(),
            dataHashValue: $element.data('anchor')
        };
    });
}

update : function (lastScroll, currentScroll) {

    if (lastScroll < currentScroll && this.elements[this.currentIndex + 1].top < currentScroll) {

        this.currentIndex++;

    } else if (lastScroll > currentScroll && this.elements[this.currentIndex - 1].bottom > currentScroll && this.currentIndex > 0) {

        this.currentIndex--;

    }

    window.location.hash = this.elements[this.currentIndex].dataHashValue;

}

scrollDebounce(this, this.update);