Skip to content

Instantly share code, notes, and snippets.

@manabuyasuda
Created January 30, 2025 08:04
Show Gist options
  • Save manabuyasuda/9412d85f5129bb3d3b694c75c49da3e2 to your computer and use it in GitHub Desktop.
Save manabuyasuda/9412d85f5129bb3d3b694c75c49da3e2 to your computer and use it in GitHub Desktop.
/**
* 指定秒数の間スクロールが止まっているかを監視するカスタムフックです。
* スクロールイベントとタイマーを組み合わせて、ユーザーのスクロール停止状態を検知します。
* @param {number} timeoutSeconds スクロールが止まったと判定するまでの秒数(デフォルト: 3秒)
* @returns {boolean} スクロールの状態
* - true: 一定時間スクロールが止まっている(指定秒数の間にスクロールなし)
* - false: スクロールが発生している(直近の指定秒数以内にスクロールあり)
*/
export function useScrollStopped(timeoutSeconds: number = 3): boolean {
// 一定時間スクロールが止まっているかどうか
const [isStopped, setIsStopped] = useState<boolean>(true);
// スクロール停止判定用のタイマー
const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
// スクロールイベントの連続発火を制御するフラグ
const ticking = useRef<boolean>(false);
useEffect(() => {
const handleScroll = () => {
// 処理が実行されていない場合だけ実行する
if (!ticking.current) {
// ブラウザの描画タイミングに合わせて処理を予約する
requestAnimationFrame(() => {
// 既存のタイマーをクリアする
if (timer.current) {
clearTimeout(timer.current);
}
// スクロール中の状態に更新する
setIsStopped(false);
// 新しいタイマーをセットする
timer.current = setTimeout(() => {
setIsStopped(true);
}, timeoutSeconds * 1000); // 秒をミリ秒に変換する
// 処理を完了し、次のイベントを受付可能にする
ticking.current = false;
});
// 状態を処理中に更新して、これ以降のイベントをブロックする
ticking.current = true;
}
};
// Scroll Junkを防止してパフォーマンスを最適化する
window.addEventListener('scroll', handleScroll, { passive: true });
return () => {
window.removeEventListener('scroll', handleScroll);
if (timer.current) {
clearTimeout(timer.current);
}
};
}, [timeoutSeconds]);
return isStopped;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment