Created
January 30, 2025 08:04
-
-
Save manabuyasuda/9412d85f5129bb3d3b694c75c49da3e2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 指定秒数の間スクロールが止まっているかを監視するカスタムフックです。 | |
* スクロールイベントとタイマーを組み合わせて、ユーザーのスクロール停止状態を検知します。 | |
* @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