Home Reference Source Test Repository

src/js/util/scroll-to-element.js

/**
 * Function to scroll to an element.
 * Based on scrollToY from http://stackoverflow.com/a/16136789/1418962
 * @param  {Element} element - The elemnt to scroll to.
 * @param  {number} to - Number indicating offset from the top of the element.
 * @param  {number} speed - Speed to scroll.
 * @param  {string} easing - Name of easing function (default: 'easeOutSine').
 */
function scrollToElement(element, to, speed, easing = 'easeOutSine') {
  let start = element.scrollTop;
  let diff = to - start;
  let currentTime = 0;

  // min time .1, max time .8 seconds
  let time = Math.max(0.1, Math.min(Math.abs(diff) / speed, 0.8));

  // easing equations from https://github.com/danro/easing-js/blob/master/easing.js
  let easingEquations = {
    easeOutSine: function (pos) {
      return Math.sin(pos * (Math.PI / 2));
    },
    easeInOutSine: function (pos) {
      return (-0.5 * (Math.cos(Math.PI * pos) - 1));
    },
    easeInOutQuint: function (oldPos) {
      let pos = oldPos * 2;
      if (pos < 1) {
        return 0.5 * Math.pow(pos, 5);
      }
      return 0.5 * (Math.pow((pos - 2), 5) + 2);
    }
  };

  // add animation loop
  function tick() {
    currentTime += 1 / 60;

    let p = currentTime / time;
    let t = easingEquations[easing](p);

    if (p < 1) {
      window.requestAnimationFrame(tick);
      element.scrollTop = start + (diff * t);
    } else {
      element.scrollTop = to;
    }
  }

  // call it once to get started
  tick();
}

export { scrollToElement };
export default scrollToElement;