Created
December 3, 2019 15:24
-
-
Save Viktor19931/a9c205198190e3cd4002f71624e7b9f1 to your computer and use it in GitHub Desktop.
scroll-polyfill
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function ScrollPolyfill() { | |
// The asynchronous tester | |
// wrapped in an iframe (will not work in SO's StackSnippet®) | |
var iframe = document.createElement('iframe'); | |
iframe.onload = function() { | |
var win = iframe.contentWindow; | |
// listen for a scroll event | |
win.addEventListener('scroll', function handler(e){ | |
// when the scroll event fires, check that we did move | |
if(win.pageXOffset < 99) { // !== 0 should be enough, but better be safe | |
attachPolyfill(); | |
} | |
// cleanup | |
document.body.removeChild(iframe); | |
}); | |
// set up our document so we can scroll | |
var body = win.document.body; | |
body.style.width = body.style.height = '1000px'; | |
win.scrollTo(10, 0); // force the event | |
win.scrollTo({left:100, behavior:'instant'}); // the one we actually test | |
}; | |
// prepare our frame | |
iframe.src = "about:blank"; | |
iframe.setAttribute('width', 1); | |
iframe.setAttribute('height', 1); | |
iframe.setAttribute('style', 'position:absolute;z-index:-1'); | |
iframe.onerror = function() { | |
console.error('failed to load the frame, try in jsfiddle'); | |
}; | |
document.body.appendChild(iframe); | |
// The Polyfill | |
function attachPolyfill() { | |
var original = window.scroll, // keep the original method around | |
animating = false, // will keep our timer's id | |
dx = 0, | |
dy = 0, | |
target = null; | |
// override our methods | |
window.scrollTo = window.scroll = function polyfilledScroll(user_opts) { | |
// if we are already smooth scrolling, we need to stop the previous one | |
// whatever the current arguments are | |
if(animating) { | |
clearAnimationFrame(animating); | |
} | |
// not the object syntax, use the default | |
if(arguments.length === 2) { | |
return original.apply(this, arguments); | |
} | |
if(!user_opts || typeof user_opts !== 'object') { | |
throw new TypeError("value can't be converted to a dictionnary"); | |
} | |
// create a clone to not mess the passed object | |
// and set missing entries | |
var opts = { | |
left: ('left' in user_opts) ? user_opts.left : window.pageXOffset, | |
top: ('top' in user_opts) ? user_opts.top : window.pageYOffset, | |
behavior: ('behavior' in user_opts) ? user_opts.behavior : 'auto', | |
}; | |
if(opts.behavior !== 'instant' && opts.behavior !== 'smooth') { | |
// parse 'auto' based on CSS computed value of 'smooth-behavior' property | |
// But note that if the browser doesn't support this variant | |
// There are good chances it doesn't support the CSS property either... | |
opts.behavior = window.getComputedStyle(document.scrollingElement || document.body) | |
.getPropertyValue('scroll-behavior') === 'smooth' ? | |
'smooth' : 'instant'; | |
} | |
if(opts.behavior === 'instant') { | |
// not smooth, just default to the original after parsing the oject | |
return original.call(this, opts.left, opts.top); | |
} | |
// update our direction | |
dx = (opts.left - window.pageXOffset) || 0; | |
dy = (opts.top - window.pageYOffset) || 0; | |
// going nowhere | |
if(!dx && !dy) { | |
return; | |
} | |
// save passed arguments | |
target = opts; | |
// save the rAF id | |
animating = anim(); | |
}; | |
// the animation loop | |
function anim() { | |
var freq = 16 / 300, // whole anim duration is approximately 300ms @60fps | |
posX, poxY; | |
if( // we already reached our goal on this axis ? | |
(dx <= 0 && window.pageXOffset <= +target.left) || | |
(dx >= 0 && window.pageXOffset >= +target.left) | |
){ | |
posX = +target.left; | |
} | |
else { | |
posX = window.pageXOffset + (dx * freq); | |
} | |
if( | |
(dy <= 0 && window.pageYOffset <= +target.top) || | |
(dy >= 0 && window.pageYOffset >= +target.top) | |
){ | |
posY = +target.top; | |
} | |
else { | |
posY = window.pageYOffset + (dx * freq); | |
} | |
// move to the new position | |
original.call(window, posX, posY); | |
// while we are not ok on both axis | |
if(posX !== +target.left || posY !== +target.top) { | |
requestAnimationFrame(anim); | |
} | |
else { | |
animating = false; | |
} | |
} | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment