RAF による mousemove 最適化の要点
問題
解決策
requestAnimationFrame (RAF) で描画を間引く
let rafId: number | null = null
element.addEventListener('mousemove', (event) => {
// 座標を取得
const coord = getCoordinate(event)
// RAF がすでにスケジュール済みならスキップ
if (rafId !== null) return
// 次フレームで描画をスケジュール
rafId = requestAnimationFrame(() => {
render(coord) // 描画処理
rafId = null
})
})RAF と mouseup の競合問題
RAF 実行前に mouseup が発火すると、状態が不整合になる
// ❌ 危険な実装
let start: Coord | null = null
mousemove: rafId = requestAnimationFrame(() => {
setSelection(start, end) // start が null の可能性
})
mouseup: start = null // RAF 実行前にリセット解決:イベント発火時に値をキャプチャ
mousemove:
const capturedStart = start
const capturedEnd = coord
rafId = requestAnimationFrame(() => {
if (!capturedStart) return // 防御的チェック
setSelection(capturedStart, capturedEnd)
})
mouseup:
if (rafId !== null) {
cancelAnimationFrame(rafId) // 未実行の RAF をキャンセル
rafId = null
}
start = null重要ポイント
RAF のスキップ:
rafId !== null で重複スケジュールを防ぐ値のキャプチャ: RAF 内で参照する変数はイベント時点でキャプチャ
RAF のクリーンアップ:
mouseup / mousedown で未実行の RAF をキャンセル防御的 null チェック: RAF 内でも念のためガード