fisshy / react-scroll

React scroll component
https://github.com/fisshy/react-scroll/blob/master/README.md
MIT License
4.36k stars 437 forks source link

Event 'begin' is not called in all cases #391

Closed GenoD closed 5 years ago

GenoD commented 5 years ago

During implementation I found that when using animateScroll I was only able to invoke the end event and not the begin event as described in the documentation. Looking through the repo I found the following results:

Currently there is no implementation to invoke the 'begin' event within animateTopScroll

const animateTopScroll = (y, options, to, target) => {
  options.data = options.data || makeData();

  window.clearTimeout(options.data.delayTimeout);

  cancelEvents.subscribe(() => {
    options.data.cancel = true;
  });

  setContainer(options);

  options.data.start = null;
  options.data.cancel = false;
  options.data.startPositionY = currentPositionY(options);
  options.data.targetPositionY = options.absolute ? y : y + options.data.startPositionY;

  if(options.data.startPositionY === options.data.targetPositionY) {
    if (events.registered['end']) {
      events.registered['end'](options.data.to, options.data.target, options.data.currentPositionY);
    }
    return;
  }

  options.data.deltaTop = Math.round(options.data.targetPositionY - options.data.startPositionY);

  options.data.duration = functionWrapper(options.duration)(options.data.deltaTop);
  options.data.duration = isNaN(parseFloat(options.data.duration)) ? 1000 : parseFloat(options.data.duration);
  options.data.to = to;
  options.data.target = target;

  let easing = getAnimationType(options);
  let easedAnimate = animateScroll.bind(null, easing, options);

  if (options && options.delay > 0) {
    options.data.delayTimeout = window.setTimeout(() => {
      requestAnimationFrameHelper.call(window, easedAnimate);
    }, options.delay);
    return;
  }

  requestAnimationFrameHelper.call(window, easedAnimate);

};

We can see that scroller invokes the begin event and also makes a call to animateTopScroll

      if(events.registered.begin) {
        events.registered.begin(to, target);
      }

      props.absolute = true;

      let scrollOffset = utils.scrollOffset(containerElement, target) + (props.offset || 0);

      /*
       * if animate is not provided just scroll into the view
       */
      if(!props.smooth) {
        if (containerElement === document) {
          window.scrollTo(0, scrollOffset);
        } else {
          containerElement.scrollTop = scrollOffset;
        }

        if(events.registered['end']) {
          events.registered['end'](to, target);
        }

        return;
      }

      /*
       * Animate scrolling
       */

      animateScroll.animateTopScroll(scrollOffset, props, to, target);

If we move the call to begin into animateTopScroll as well as into the the if(!props.smooth) block, we can ensure this event is always called when set by devs.