Last active
February 18, 2021 17:38
-
-
Save vviikk/39cb5208694094b1930190c3c90a3255 to your computer and use it in GitHub Desktop.
Smooth scrolling you can add inject into any Nativefier app
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
// ==UserScript== | |
// @name Smoothscroll | |
// @author Creec Winceptor | |
// @description Smooth scrolling on pages using javascript | |
// @namespace https://greasyfork.org/users/3167 | |
// @include * | |
// @version 9.6 | |
// ==/UserScript== | |
const Smoothscroll = {}; | |
//settings | |
Smoothscroll.Smoothness = 1.75; | |
Smoothscroll.Acceleration = 1.5; | |
//debug | |
Smoothscroll.Debug = false; | |
//autodetected | |
Smoothscroll.Refreshrate = 60; | |
//scrolling and animation | |
const ScrollSubpixels = (element, newvalue) => { | |
if (newvalue != undefined) { | |
element.scrollsubpixels = newvalue; | |
return newvalue; | |
} | |
else { | |
const olddelta = element.scrollsubpixels; | |
if (olddelta != undefined) { | |
return olddelta; | |
} | |
return 0; | |
} | |
}; | |
function ScrollPixels(element, newvalue) { | |
if (newvalue != undefined) { | |
element.scrollpixels = newvalue; | |
ScrollSubpixels(element, 0); | |
return newvalue; | |
} | |
else { | |
const olddelta = element.scrollpixels; | |
if (olddelta != undefined) { | |
return olddelta; | |
} | |
return 0; | |
} | |
} | |
const last = 0; | |
const AnimateScroll = (target, now) => { | |
const scrollsubpixels = ScrollSubpixels(target); | |
const scrollpixels = ScrollPixels(target); | |
if (Smoothscroll.Debug) { | |
console.log(`scrollpixels: ${scrollpixels}`); | |
} | |
if (Smoothscroll.Debug) { | |
console.log("target: ", target); | |
if (target == document.documentElement) { | |
console.log("document.documentElement"); | |
} | |
} | |
let scrolldirection = 0; | |
if (scrollpixels > 0) { | |
scrolldirection = 1; | |
} | |
if (scrollpixels < 0) { | |
scrolldirection = -1; | |
} | |
const scrollratio = 1 - Smoothscroll.Refreshrate ** (-1 / (Smoothscroll.Refreshrate * Smoothscroll.Smoothness)); | |
const scrollrate = scrollpixels * scrollratio; | |
if (Math.abs(scrollpixels) > 1) { | |
const fullscrolls = Math.floor(Math.abs(scrollrate)) * scrolldirection; | |
const scrollsubpixelsadded = scrollrate - fullscrolls; | |
const additionalscrolls = Math.floor(Math.abs(scrollsubpixels + scrollsubpixelsadded)) * scrolldirection; | |
const scrollsubpixelsleft = scrollsubpixels + scrollsubpixelsadded - additionalscrolls; | |
ScrollPixels(target, scrollpixels - fullscrolls - additionalscrolls); | |
ScrollSubpixels(target, scrollsubpixelsleft); | |
const scrolldelta = fullscrolls + additionalscrolls; | |
if (Smoothscroll.Debug) { | |
console.log(`scrolldelta: ${scrolldelta}`); | |
} | |
target.style.scrollBehavior = "auto"; // fix for pages with changed scroll-behavior | |
target.scrollTop = target.scrollTop + scrolldelta; | |
if (Smoothscroll.Debug) { | |
console.log(`target.scrollTop: ${target.scrollTop}`); | |
} | |
target.scrollanimated = true; | |
window.requestAnimationFrame(() => { | |
AnimateScroll(target); | |
}); | |
} else { | |
window.requestAnimationFrame(() => { | |
ScrollPixels(target, 0); | |
}); | |
target.scrollanimated = false; | |
} | |
}; | |
Smoothscroll.Stop = target => { | |
if (target) { | |
ScrollPixels(target, 0); | |
} | |
} | |
Smoothscroll.Start = (target, scrollamount) => { | |
if (target) { | |
const scrollpixels = ScrollPixels(target); | |
ScrollPixels(target, scrollamount); | |
if (!target.scrollanimated) { | |
AnimateScroll(target); | |
} | |
} | |
} | |
if (typeof module !== 'undefined') { | |
module.exports = Smoothscroll; | |
} | |
const CanScroll = (element, dir) => { | |
if (dir < 0) { | |
return element.scrollTop > 0; | |
} | |
if (dir > 0) { | |
if (element == document.body) { | |
if (element.scrollTop == 0) { | |
element.scrollTop = 3; | |
if (element.scrollTop == 0) { | |
return false; | |
} | |
element.scrollTop = 0; | |
} | |
return Math.round(element.clientHeight + element.scrollTop) < (element.offsetHeight); | |
} | |
return Math.round(element.clientHeight + element.scrollTop) < (element.scrollHeight); | |
} | |
}; | |
const HasScrollbar = element => { | |
//TODO: problem with webkit, body not scrollable? | |
if (element == window || element == document) { | |
return false; | |
} | |
if (element == document.body) { | |
return window.getComputedStyle(document.body)['overflow-y'] != "hidden"; | |
} | |
//THANK YOU TO: https://tylercipriani.com/blog/2014/07/12/crossbrowser-javascript-scrollbar-detection/ | |
if (element == document.documentElement) { | |
return window.innerWidth > document.documentElement.clientWidth; | |
} else { | |
//return (element.clientWidth-element.clientWidth)>0; | |
const style = window.getComputedStyle(element); | |
return style['overflow-y'] != "hidden" && style['overflow-y'] != "visible"; | |
} | |
}; | |
function Scrollable(element, dir) { | |
//TODO: problem with webkit, body not scrollable? | |
if (element == document.body) { | |
//return false; | |
} | |
const scrollablecheck = CanScroll(element, dir); | |
if (!scrollablecheck) { | |
if (Smoothscroll.Debug) { | |
console.log(`scrollablecheck: ${scrollablecheck}`); | |
} | |
return false; | |
} | |
const scrollbarcheck = HasScrollbar(element); | |
if (!scrollbarcheck) { | |
if (Smoothscroll.Debug) { | |
console.log(`scrollbarcheck: ${scrollbarcheck}`); | |
} | |
return false; | |
} | |
if (Smoothscroll.Debug) { | |
console.log(`scrollablecheck: ${scrollablecheck}`); | |
console.log(`scrollbarcheck: ${scrollbarcheck}`); | |
} | |
return true; | |
} | |
function GetPath(e) { | |
if (e.path) { | |
return e.path; | |
} | |
if (e.composedPath) { | |
return e.composedPath(); | |
} | |
if (Smoothscroll.Debug) { | |
console.log("Smoothscroll: e.path is undefined"); | |
} | |
return null; | |
} | |
function GetTarget(e) { | |
const direction = e.deltaY; | |
const nodes = GetPath(e); | |
if (!nodes) { | |
return null; | |
} | |
if (Smoothscroll.Debug) { | |
console.log("nodes: "); | |
console.log(nodes); | |
console.log("target: "); | |
} | |
for (let i = 0; i < (nodes.length); i++) { | |
const node = nodes[i]; | |
if (Smoothscroll.Debug) { | |
console.log(node); | |
} | |
if (Scrollable(node, direction)) { | |
if (Smoothscroll.Debug) { | |
console.log("true"); | |
} | |
return node; | |
} | |
} | |
if (Smoothscroll.Debug) { | |
console.log("false"); | |
} | |
return null; | |
} | |
function GetStyleProperty(el, styleprop) { | |
if (window.getComputedStyle) { | |
var heightprop = document.defaultView.getComputedStyle(el, null).getPropertyValue(styleprop); | |
if (heightprop) { | |
return parseInt(heightprop); | |
} | |
} | |
else if (el.currentStyle) { | |
var heightprop = el.currentStyle[styleprop.encamel()]; | |
if (heightprop) { | |
return parseInt(heightprop); | |
} | |
} | |
return null; | |
} | |
//mouse event scroll handlers | |
function StopScroll(e) { | |
const nodes = GetPath(e); | |
if (!nodes) { | |
return null; | |
} | |
for (let i = 0; i < (nodes.length); i++) { | |
const node = nodes[i]; | |
Smoothscroll.Stop(node); | |
} | |
} | |
function StartScroll(e, target) { | |
if (e.defaultPrevented) { | |
return true; | |
} | |
else { | |
let delta = e.deltaY; | |
if (e.deltaMode && e.deltaMode == 1) { | |
const line = GetStyleProperty(target, 'line-height'); | |
if (line && line > 0) { | |
delta = e.deltaY * line; | |
} | |
} | |
if (e.deltaMode && e.deltaMode == 2) { | |
const page = target.clientHeight; | |
if (page && page > 0) { | |
delta = e.deltaY * page; | |
} | |
} | |
const scrollpixels = ScrollPixels(target); | |
const accelerationratio = Math.sqrt(Math.abs(scrollpixels / delta * Smoothscroll.Acceleration)); | |
const acceleration = Math.round(delta * accelerationratio); | |
Smoothscroll.Start(target, scrollpixels + delta + acceleration); | |
e.preventDefault(); | |
} | |
} | |
//mouse event call handlers | |
function WheelEvent(e) { | |
const target = GetTarget(e); | |
if (target) { | |
StartScroll(e, target); | |
} | |
} | |
function ClickEvent(e) { | |
StopScroll(e); | |
} | |
let now0 = null; | |
function Fps(now) { | |
if (now0 != null) { | |
Smoothscroll.Refreshrate = 1000 / (now - now0); | |
} | |
now0 = now; | |
window.requestAnimationFrame(Fps); | |
} | |
(({ | |
ignoreIframe = true, | |
debug= false, | |
} = {}) => { | |
if (window.top != window.self && ignoreIframe) { | |
console.log("Smoothscroll: ignoring iframe"); | |
return null; | |
} | |
if (window.Smoothscroll && window.Smoothscroll.Loaded) { | |
//console.log("Smoothscroll: already loaded"); | |
return null; | |
} | |
if (!window.requestAnimationFrame) { | |
window.requestAnimationFrame = | |
window.mozRequestAnimationFrame || | |
window.webkitRequestAnimationFrame; | |
} | |
document.documentElement.addEventListener("wheel", e => { | |
WheelEvent(e); | |
if (Smoothscroll.Debug) { | |
console.log(e); | |
} | |
}, { passive: false }); | |
document.documentElement.addEventListener("mousedown", e => { | |
ClickEvent(e); | |
if (Smoothscroll.Debug) { | |
console.log(e); | |
} | |
}); | |
// window.addEventListener("pointermove", console.log, false); | |
document.documentElement.addEventListener("pointercancel", console.log, true); | |
window.Smoothscroll = Smoothscroll; | |
window.Smoothscroll.Loaded = true; | |
window.requestAnimationFrame(Fps); | |
console.log("Smoothscroll: loaded"); | |
})({ | |
ignoreIframe: false, | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment