問題
mousemove イベントは高頻度で発火(ミリ秒単位)
毎回 DOM 操作(選択範囲レンダリング)すると性能劣化
解決策
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 内でも念のためガード
効果
描画頻度: 毎ミリ秒 → 60fps(約 16ms ごと)
不要な DOM 操作を削減
滑らかな UI 更新