Skip to content

Instantly share code, notes, and snippets.

@maxrolon
Last active September 16, 2016 16:21
Show Gist options
  • Save maxrolon/bc6b818b193d6813345e569667b47e83 to your computer and use it in GitHub Desktop.
Save maxrolon/bc6b818b193d6813345e569667b47e83 to your computer and use it in GitHub Desktop.
Scroll handlers with RAF
let requestFrame = window.requestAnimationFrame
let cancelFrame = window.cancelAnimationFrame
let scrollChanged, y, prevY = -1, idle = true, queue = [], timeout, tickId, init = false
if (!requestFrame) {
['ms', 'moz', 'webkit', 'o'].every(prefix => {
requestFrame = window[prefix + 'RequestAnimationFrame'];
cancelFrame = window[prefix + 'CancelAnimationFrame'] ||
window[prefix + 'CancelRequestAnimationFrame'];
return !requestFrame;
})
}
const isSupported = !!requestFrame
const enable = () => {
window.addEventListener('scroll', debounce)
document.body.addEventListener('touchmove', debounce)
}
const disable = () => {
window.removeEventListener('scroll', debounce)
document.body.removeEventListener('touchmove', debounce)
}
const debounce = () => {
if (!tickId){
disable()
tick()
}
}
const tick = () => {
tickId = requestFrame(handleScroll)
}
const handleScroll = () => {
y = window.pageYOffset
queue.forEach( fn => fn(y, prevY) )
scrollChanged = false
if (prevY != y){
scrollChanged = true
prevY = y
}
if (scrollChanged){
clearTimeout(timeout)
timeout = null
} else if (!timeout){
timeout = setTimeout(detectIdle, 200)
}
tick()
}
const detectIdle = () => {
cancelFrame(tickId)
tickId = null
enable()
}
export default cb => {
if (isSupported){
queue.push(cb)
//Trigger scroll event immediately
if (!init){
init = true
debounce()
enable()
}
} else {
console.warn('Request Animation Frame not supported')
}
}
@maxrolon
Copy link
Author

maxrolon commented Sep 16, 2016

A simple, performance optimized replacement for window.addEventListener('scroll', function(e) {...

The premiss for this module is to utilize animation frames to trigger scroll callbacks in the appropriate render pipeline location of each frame.

Usage:

import scroll from './../<location>'

scroll( (y, prevY) => {
}

The callback passed to the scroll function will get passed the current scrollY value (param 1) and the previous scrollY value (param 2). From this you will be able to ascertain scroll direction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment