scrollイベントが呼ばれすぎることへの対処
onscrollを監視していると大量にeventが来てパフォーマンスが劣化するというのはよく聞く話で、 underscore.jsなどでイベントをthrottleやdebounceさせて対処していることが多い。 ただ、scrollに応じてスタイルを変更させたりする場合は、0.1秒くらい遅れがあると視覚的にカクカクしがちで、仕方なくsetTimeout(fn, 0)でthrottleさせることも多いと思う。
scrollイベントは画面のフラッシュレートと同期していないので、マウスやトラックパッドの動きに応じて大量に呼ばれていて、setTimeoutも画面のフレッシュレートとは関係なく内部の実行タイマーに応じて動いている。よって、setTimeout(fn, 0)でthrottleしてもまだ無駄が多い。実験してみたところ、requestAnimationFrameを使うと少しだけマシになることがわかった。
結論から言うと、スクロールと画面の同期遅れを最小限にしたい場合はsetTimeoutではなくrequestAnimationを使ったほうがsetTimeout(fn, 0)より良くて、画面のチラツキが気にならないような場面ではsetTimeout(fn, 100)くらいを使うと良いと思う。
[Log] native: 484 [Log] throttled: 429 [Log] smartly throttled: 341
これは、下記のテストコードを適当なページで走らせて上下にスクロールさせた結果。
setInterval(function () { console.log("native: %d", nNative); console.log("throttled: %d", nThrottled); console.log("smartly throttled: %d", nSmartThrottled); }, 1000); var nNative = 0; window.addEventListener('scroll', function () { nNative++; }, false); var tid; var nThrottled = 0; window.addEventListener('scroll', function (event) { clearTimeout(tid); tid = setTimeout(function () { nThrottled++; }, 0); }, false); var nSmartThrottled = 0; function callback(e) { nSmartThrottled++; } var frameId; function handleScroll(event) { cancelAnimationFrame(frameId); frameId = requestAnimationFrame(function () { callback(event); }); } window.addEventListener('scroll', handleScroll, false);