Make smooth-scroll smoother
This commit is contained in:
		
							parent
							
								
									b5a85ad059
								
							
						
					
					
						commit
						514f91f8dc
					
				
					 1 changed files with 30 additions and 65 deletions
				
			
		| 
						 | 
					@ -2,10 +2,10 @@ import * as doms from 'shared/utils/dom';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const SCROLL_DELTA_X = 48;
 | 
					const SCROLL_DELTA_X = 48;
 | 
				
			||||||
const SCROLL_DELTA_Y = 48;
 | 
					const SCROLL_DELTA_Y = 48;
 | 
				
			||||||
const SMOOTH_SCROLL_DURATION = 150;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// dirty way to store scrolling state on globally
 | 
					// dirty way to store scrolling state on globally
 | 
				
			||||||
let scrolling = [false];
 | 
					let scrolling = false;
 | 
				
			||||||
 | 
					let lastTimeoutId = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const isScrollableStyle = (element) => {
 | 
					const isScrollableStyle = (element) => {
 | 
				
			||||||
  let { overflowX, overflowY } = window.getComputedStyle(element);
 | 
					  let { overflowX, overflowY } = window.getComputedStyle(element);
 | 
				
			||||||
| 
						 | 
					@ -51,65 +51,27 @@ const scrollTarget = () => {
 | 
				
			||||||
  return window.document.documentElement;
 | 
					  return window.document.documentElement;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const resetScrolling = () => {
 | 
				
			||||||
 | 
					  scrolling = false;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SmoothScroller {
 | 
					class SmoothScroller {
 | 
				
			||||||
  constructor(element, repeat) {
 | 
					  constructor(element) {
 | 
				
			||||||
    this.element = element;
 | 
					    this.element = element;
 | 
				
			||||||
    this.repeat = repeat;
 | 
					 | 
				
			||||||
    this.scrolling = scrolling;
 | 
					 | 
				
			||||||
    if (repeat) {
 | 
					 | 
				
			||||||
      this.easing = SmoothScroller.linearEasing;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      this.easing = SmoothScroller.inOutQuadEasing;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  scroll(x, y) {
 | 
					  scroll(x, y) {
 | 
				
			||||||
    if (this.scrolling[0]) {
 | 
					    window.scrollTo({
 | 
				
			||||||
      return;
 | 
					      left: x,
 | 
				
			||||||
 | 
					      top: y,
 | 
				
			||||||
 | 
					      behavior: 'smooth',
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    scrolling = true;
 | 
				
			||||||
 | 
					    if (lastTimeoutId) {
 | 
				
			||||||
 | 
					      clearTimeout(lastTimeoutId);
 | 
				
			||||||
 | 
					      lastTimeoutId = null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    scrolling[0] = true;
 | 
					    lastTimeoutId = setTimeout(resetScrolling, 100);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    this.startX = this.element.scrollLeft;
 | 
					 | 
				
			||||||
    this.startY = this.element.scrollTop;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    this.targetX = x;
 | 
					 | 
				
			||||||
    this.targetY = y;
 | 
					 | 
				
			||||||
    this.distanceX = x - this.startX;
 | 
					 | 
				
			||||||
    this.distanceY = y - this.startY;
 | 
					 | 
				
			||||||
    this.timeStart = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    window.requestAnimationFrame(this.loop.bind(this));
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  loop(time) {
 | 
					 | 
				
			||||||
    if (!this.timeStart) {
 | 
					 | 
				
			||||||
      this.timeStart = time;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let elapsed = time - this.timeStart;
 | 
					 | 
				
			||||||
    let v = this.easing(elapsed / SMOOTH_SCROLL_DURATION);
 | 
					 | 
				
			||||||
    let nextX = this.startX + this.distanceX * v;
 | 
					 | 
				
			||||||
    let nextY = this.startY + this.distanceY * v;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    window.scrollTo(nextX, nextY);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (elapsed < SMOOTH_SCROLL_DURATION) {
 | 
					 | 
				
			||||||
      window.requestAnimationFrame(this.loop.bind(this));
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      scrolling[0] = false;
 | 
					 | 
				
			||||||
      this.element.scrollTo(this.targetX, this.targetY);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  static inOutQuadEasing(t) {
 | 
					 | 
				
			||||||
    if (t < 1) {
 | 
					 | 
				
			||||||
      return t * t;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return -(t - 1) * (t - 1) + 1;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  static linearEasing(t) {
 | 
					 | 
				
			||||||
    return t;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -123,9 +85,9 @@ class RoughtScroller {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const scroller = (element, smooth, repeat) => {
 | 
					const scroller = (element, smooth) => {
 | 
				
			||||||
  if (smooth) {
 | 
					  if (smooth) {
 | 
				
			||||||
    return new SmoothScroller(element, repeat);
 | 
					    return new SmoothScroller(element);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  return new RoughtScroller(element);
 | 
					  return new RoughtScroller(element);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -135,32 +97,35 @@ const getScroll = () => {
 | 
				
			||||||
  return { x: target.scrollLeft, y: target.scrollTop };
 | 
					  return { x: target.scrollLeft, y: target.scrollTop };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const scrollVertically = (count, smooth, repeat) => {
 | 
					const scrollVertically = (count, smooth) => {
 | 
				
			||||||
  let target = scrollTarget();
 | 
					  let target = scrollTarget();
 | 
				
			||||||
  let x = target.scrollLeft;
 | 
					  let x = target.scrollLeft;
 | 
				
			||||||
  let y = target.scrollTop + SCROLL_DELTA_Y * count;
 | 
					  let y = target.scrollTop + SCROLL_DELTA_Y * count;
 | 
				
			||||||
  if (repeat && smooth) {
 | 
					  if (scrolling) {
 | 
				
			||||||
    y = target.scrollTop + SCROLL_DELTA_Y * count * 4;
 | 
					    y = target.scrollTop + SCROLL_DELTA_Y * count * 4;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  scroller(target, smooth, repeat).scroll(x, y);
 | 
					  scroller(target, smooth).scroll(x, y);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const scrollHorizonally = (count, smooth, repeat) => {
 | 
					const scrollHorizonally = (count, smooth) => {
 | 
				
			||||||
  let target = scrollTarget();
 | 
					  let target = scrollTarget();
 | 
				
			||||||
  let x = target.scrollLeft + SCROLL_DELTA_X * count;
 | 
					  let x = target.scrollLeft + SCROLL_DELTA_X * count;
 | 
				
			||||||
  let y = target.scrollTop;
 | 
					  let y = target.scrollTop;
 | 
				
			||||||
  if (repeat && smooth) {
 | 
					  if (scrolling) {
 | 
				
			||||||
    y = target.scrollTop + SCROLL_DELTA_Y * count * 4;
 | 
					    y = target.scrollTop + SCROLL_DELTA_Y * count * 4;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  scroller(target, smooth, repeat).scroll(x, y);
 | 
					  scroller(target, smooth).scroll(x, y);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const scrollPages = (count, smooth, repeat) => {
 | 
					const scrollPages = (count, smooth) => {
 | 
				
			||||||
  let target = scrollTarget();
 | 
					  let target = scrollTarget();
 | 
				
			||||||
  let height = target.clientHeight;
 | 
					  let height = target.clientHeight;
 | 
				
			||||||
  let x = target.scrollLeft;
 | 
					  let x = target.scrollLeft;
 | 
				
			||||||
  let y = target.scrollTop + height * count;
 | 
					  let y = target.scrollTop + height * count;
 | 
				
			||||||
  scroller(target, smooth, repeat).scroll(x, y);
 | 
					  if (scrolling) {
 | 
				
			||||||
 | 
					    y = target.scrollTop + height * count * 4;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  scroller(target, smooth).scroll(x, y);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const scrollTo = (x, y, smooth) => {
 | 
					const scrollTo = (x, y, smooth) => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Reference in a new issue