Skip to content

Instantly share code, notes, and snippets.

@robksawyer
Last active September 13, 2022 04:48
Show Gist options
  • Save robksawyer/4c6986969dab3f710c2d6cc5e15f7d0d to your computer and use it in GitHub Desktop.
Save robksawyer/4c6986969dab3f710c2d6cc5e15f7d0d to your computer and use it in GitHub Desktop.
A hook for checking the scroll speed with smooth scrollbar.
/**
* @file useIsoLayoutEffect.js
* Hook to suppress useLayoutEffect error on SSR.
*/
import { useLayoutEffect, useEffect } from 'react';
const useIsoLayoutEffect =
typeof window !== 'undefined' ? useLayoutEffect : useEffect;
export { useIsoLayoutEffect as useLayoutEffect };
/**
* @file useSmoothScrollSpeed.js
* @see https://github.com/idiotWu/smooth-scrollbar/issues/199
*/
import React from 'react';
import { useLayoutEffect } from '@/hooks/useIsoLayoutEffect';
import { singletonHook } from 'react-singleton-hook';
import { useStore } from '@/store';
const initState = 0;
const useSmoothScrollSpeedImpl = () => {
const [speed, setSpeed] = React.useState(initState);
const { scrollbar: scrollbarRef } = useStore();
const lastTime = React.useRef(0);
const lastOffset = React.useRef(0);
const reduceAmount = React.useRef(0);
/**
* checkScrollSpeed
* @see https://stackoverflow.com/questions/22593286/detect-measure-scroll-speed
* @param {object} settings
* @return {number} is the delta scroll amount
*/
const checkScrollSpeed = React.useCallback(settings => {
const defaultSettings = { delay: 50, y: 0 };
const _settings = { ...defaultSettings, ...settings };
let current = Date.now();
let offset = _settings.y;
let duration = current - lastTime.current;
if (!duration || offset === lastOffset) return;
if (duration > _settings.delay) {
reduceAmount.current += duration - 1;
duration -= duration - 1;
}
let velocity = (offset - lastOffset.current) / duration;
lastTime.current = current;
lastOffset.current = offset;
return {
offset,
time: current - reduceAmount.current,
reduce: reduceAmount.current,
speed: Math.abs(velocity),
};
}, []);
const onScroll = React.useCallback(
e => {
const res = checkScrollSpeed({ y: e.offset.y });
const { speed } = res;
setSpeed(speed);
},
[setSpeed, checkScrollSpeed],
);
useLayoutEffect(() => {
const scrollbar = scrollbarRef.current;
scrollbar.addListener(onScroll);
return () => scrollbar.removeListener(onScroll);
}, [scrollbarRef, onScroll]);
React.useEffect(() => {
// Reset to zero
const timer = setTimeout(() => setSpeed(0), 1000);
return () => clearTimeout(timer);
}, [speed]);
return speed;
};
export const useSmoothScrollSpeed = singletonHook(
initState,
useSmoothScrollSpeedImpl,
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment