頻度が高いイベントへの対応
イベントにはclick
のように扱いやすいものがある一方で、scroll
やmousemove
などのように大量に実行されてしまう厄介なものもあります。そのようなイベントをそのまま実装してしまうと不要な呼び出しが多く、動作が重たくなる原因になってしまうので、適度に間引いて処理をしたほうがブラウザに優しいです。
最近のブラウザでは、指定した関数を再描画の前に呼び出してくれるrequestAnimationFrameという便利なメソッドがあるので、それを使用して最適化する例をいくつか載せています。
今では少ないかと思いますが、requestAnimationFrameに対応していない、IE9以前/Android4.3以前などのブラウザにも対応する必要がある場合は、非対応のときにsetTimeoutに置き換えてくれるPolyfillのようなもの使うか、別の実装を検討する必要があります。
Can I use | requestAnimationFrame 0pxscroll
var isRunning = false
window.addEventListener('scroll', function() {
// 呼び出されるまで何もしない
if (!isRunning) {
isRunning = true
// 描画する前のタイミングで呼び出してもらう
window.requestAnimationFrame(function() {
// ここでなにか処理をする
console.log(window.pageYOffset)
isRunning = false
})
}
})
mouse
var isDragging = false
var startPosition = null
var lastPosition = null
var requestId = null
var circle = document.getElementById('circle')
var translate = function(x, y) {
circle.style.transform = 'translate3d(' + x + 'px,' + y + 'px,0)'
}
// この関数で描画する
var animate = function() {
var x = lastPosition.x - startPosition.x
var y = lastPosition.y - startPosition.y
translate(x, y)
requestId = window.requestAnimationFrame(animate)
}
// 最後にリセット
var leave = function() {
isDragging = false
translate(0, 0)
// もう呼んでほしくないのでキャンセル
if (requestId) {
window.cancelAnimationFrame(requestId)
}
}
// 円上でマウスが押されたらアニメーション開始
circle.addEventListener('mousedown', function(e) {
isDragging = true
startPosition = { x: e.clientX, y: e.clientY }
lastPosition = { x: e.clientX, y: e.clientY }
animate()
})
// 動いてるときは座標を更新して
circle.addEventListener('mousemove', function(e) {
if (isDragging) {
lastPosition = { x: e.clientX, y: e.clientY }
}
})
// 離れたら手を引く
circle.addEventListener('mouseup', function(e) {
leave()
})
circle.addEventListener('mouseleave', function(e) {
leave()
})