Skip to content

Instantly share code, notes, and snippets.

@vviikk
Last active February 18, 2021 17:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vviikk/39cb5208694094b1930190c3c90a3255 to your computer and use it in GitHub Desktop.
Save vviikk/39cb5208694094b1930190c3c90a3255 to your computer and use it in GitHub Desktop.
Smooth scrolling you can add inject into any Nativefier app
// ==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