September 1, 2024Programming

Throttle在 Full Page Slider 中的應用

本網Home Page是一個典型的Full Page Slider。當window處於大於1024px的時候,會處於一個scrollbar被隱藏、網頁scroll/wheel行為完全由js接管的狀態(使用家不能自由的scroll,而是由js控制剛剛好scroll到特定的位置,即向上滑就scoll到上一section、向下滑就到下一section)。

但是必須注意的是,scroll/wheel event是一種可以連續觸發的事件,而且頻率極高。用家用滑鼠滾輪滑一下即可觸發數十次。如果每次事件都引發移動,除了造成更高性能消耗外,還會一下子就到滑到底。

所以必須使用節流(Throttle)的形式來限制scroll/wheel event對Full Page Slider的影響,js (react) 代碼如下:

const lastTimeRef = useRef<number>(0); //// HANDLE WHEEL EVENT const sectionWheelHandler = useCallback( (event: WheelEvent) => { event.preventDefault(); const currentTime = Date.now(); const animationBreak = 1800; const notRapidSuccession = currentTime > lastTimeRef.current + animationBreak; if (notRapidSuccession) { if (event.deltaY < 0) { goToPrevSection(); } else if (event.deltaY >= 0) { goToNextSection(); } lastTimeRef.current = currentTime; } }, [goToPrevSection, goToNextSection] );

這個function是限制在一定時長內一個行為只觸發一次,這裏的行為則是goToPrevSection()goToNextSection()。我把上一次觸發行為的timestamp存在lastTimeRef中,每次事件則計算現在的timestamp是否大於lastTimeRef animationBreak的總時長,如果不是的話則不觸發行為。當然可能有人會問,為什麼上一次的timestamp要存在ref中?這涉及到react 過期閉包(stale closure)的問題,有時間會出一篇文章分析。

animationBreak定義的1800ms 是經過筆者測試過macbook touchpad向下划會持續觸發wheel事件的時長,而一般滑鼠滾輪觸發的時長也不會比這更長(除了筆者愛用的mx master 3,它可以設定為超長滑動模式,甚至可以持續幾秒)

如果animationBreak太短,會造成超過一次goToNextSection(),有時會造成不好體驗(即划一次卻連續移動到下下個section這樣的情況)。考慮到用家看一個section 的文字1.8秒以上也算相當合理,所以這裡設為比較平衡的1.8秒。

一個典型的Animation Break太短的情況是UNIQLO日本的官網首頁。如果用Macbook Pro的touchpad向下划一次會至少向下移動兩行,用家體驗會受到很大影響。