scroll_tracker/ScrollTracker.js

/**
  Keeps track of the furthest point on page that user has scrolled to
  @memberof module:Viki
**/
class ScrollTracker {
  /**
    @constructor
  **/
  constructor() {
    this.storage = [];
    this.lastPos = Infinity;
    this.startPos = window.scrollY;
    this.direction = 1;
    this.thresh = 0;
    this.scrollTrackHandler = this.scrollTrackHandler.bind(this);
    this.waiting = true;
  }

  /**
    Start tracking
  **/
  init() {
    this.startPos = window.scrollY;
    this.store(this.startPos);
    window.addEventListener('scroll', this.scrollTrackHandler);
  }

  scrollTrackHandler() {
    if (this.waiting) {
      window.requestAnimationFrame(() => {
        const currPos = window.scrollY;

        // Save coordinate if direction changes
        if (this.lastPos !== Infinity && (currPos - this.lastPos) * this.direction < this.thresh) {
          this.store(currPos);
          this.startPos = currPos;
          this.direction *= -1;
        }
        this.lastPos = currPos;

        this.waiting = true;
      });
    }
    this.waiting = false;
  }

  /**
    Save scroll y coordinate
    @param {Number} y y-coordinate to store
  **/
  store(y) {
    this.storage.push(y);
  }

  /**
    Percentage of document that appears on user's viewport
    @returns {Number}
  **/
  percentageSeen() {
    this.store(window.scrollY);
    const furthestScrolled = Math.max.apply(null, this.storage) || 0;
    const viewportHeight = (window.innerHeight || document.documentElement.clientHeight);
    const furthestViewport = furthestScrolled + viewportHeight;
    const docRect = document.documentElement.getBoundingClientRect();
    const docHeight = docRect.height;
    const res = Math.ceil((furthestViewport / docHeight) * 100);
    return res;
  }

  /**
    Percentage of document that appears on user's viewport rounded to given granularity
    @param {Number} [granularity=10]  - granularity of rounding
    @returns {Number}
  **/
  percentageSeenRounded(granularity = 10) {
    const seen = this.percentageSeen();
    const res = Math.round(seen / granularity) * granularity;
    return res;
  }
}

module.exports = ScrollTracker;