Scroll by mouse drag

Scroll the page while holding chosen mouse button anywhere on the website

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Greasemonkey lub Violentmonkey.

You will need to install an extension such as Tampermonkey to install this script.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana będzie instalacja rozszerzenia Tampermonkey lub Userscripts.

You will need to install an extension such as Tampermonkey to install this script.

Aby zainstalować ten skrypt, musisz zainstalować rozszerzenie menedżera skryptów użytkownika.

(Mam już menedżera skryptów użytkownika, pozwól mi to zainstalować!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Musisz zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

(Mam już menedżera stylów użytkownika, pozwól mi to zainstalować!)

// ==UserScript==
// @name        Scroll by mouse drag
// @namespace   FawayTT
// @description Scroll the page while holding chosen mouse button anywhere on the website
// @match       *://*/*
// @version     1.2
// @author      FawayTT
// @license     MIT
// @homepage    https://github.com/FawayTT/userscripts
// @require     https://openuserjs.org/src/libs/sizzle/GM_config.js
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_registerMenuCommand
// @grant       GM_openInTab
// ==/UserScript==

GM_registerMenuCommand('Settings', opencfg);

const configcss = `height: 40rem;
width: 35rem;
border-radius: 10px;
z-index: 9999999;
position: fixed;
`;

const defaults = {
  touchArea: `width: 100%;
height: 100%;
position: fixed;
left: 50%;
top: 0;
pointer-events: none;
transform: translateX(-50%);`,
};

let gmc = new GM_config({
  id: 'config',
  title: 'Scroll by mouse drag settings',
  fields: {
    scrollButton: {
      section: ['Behavior'],
      label: 'Scroll button',
      labelPos: 'left',
      type: 'select',
      default: 'Left', // Default value for Blue
      options: ['Left', 'Right', 'MiddleClick'],
    },
    touchArea: {
      label: 'Touchpad zone position custom CSS (by default, its invisible and centered in the middle of the page)',
      labelPos: 'left',
      type: 'textarea',
      default: defaults.touchArea,
    },
    msClick: {
      label: 'Number of ms before scroll starts',
      labelPos: 'left',
      type: 'number',
      default: 100,
    },
    textSelectable: {
      label: 'Turn off when selecting text',
      labelPos: 'right',
      type: 'checkbox',
      default: true,
    },
    scrollSpeed: {
      section: ['Speed'],
      label: 'Speed of scroll',
      labelPos: 'left',
      type: 'number',
      default: 1.5,
    },
    blacklistedSites: {
      section: ['Sites'],
      label: 'Blacklisted sites (separated by comma)',
      labelPos: 'right',
      type: 'text',
      default: '',
    },
    whitelistEnabled: {
      label: 'Whitelist enabled',
      labelPos: 'right',
      type: 'checkbox',
      default: false,
    },
    whitelistedSites: {
      label: 'Whitelisted sites (separated by comma)',
      labelPos: 'right',
      type: 'text',
      default: '',
    },
    url: {
      section: ['Support'],
      label: 'https://github.com/FawayTT/userscripts',
      type: 'button',
      click: () => {
        GM_openInTab('https://github.com/FawayTT/userscripts');
      },
    },
  },
  events: {
    save: function () {
      gmc.close();
    },
    init: onInit,
  },
});

const scrollDiv = document.createElement('div');
let animationDuration = 100;
let isMouseDown = false;
let startY = 0;
let startX = 0;
let startTimeout;
let distance = 0;
let isScrolling;
let clicks = 0;
let textSelectable;
let button;
let msClick;
let scrollSpeed = 1.5;

const animateScroll = (distance) => {
  const startTime = performance.now();
  const startScrollY = window.scrollY;
  let extraScroll = true;

  const scroll = (timestamp) => {
    const elapsedTime = timestamp - startTime;
    const progress = Math.min(elapsedTime / animationDuration, 1);
    const scrollAmount = startScrollY + distance * progress;
    if (window.scrollY < 50 && distance < 0) {
      return;
    }
    window.scrollTo(0, Math.abs(scrollAmount));

    if (progress < 1) {
      requestAnimationFrame(scroll);
    } else {
      isScrolling = false;
      if (extraScroll) {
        extraScroll = false;
        window.scrollBy({ top: distance * 0.5, behavior: 'smooth' });
      }
    }
  };

  requestAnimationFrame(scroll);
};

function onInit() {
  const whitelistEnabled = gmc.get('whitelistEnabled');
  const blacklistedSites = gmc.get('blacklistedSites');
  const whitelistedSites = gmc.get('whitelistedSites');
  button = gmc.get('scrollButton');
  textSelectable = gmc.get('textSelectable');
  msClick = gmc.get('msClick');
  scrollSpeed = gmc.get('scrollSpeed');

  switch (button) {
    case 'Left':
      button = 0;
      break;
    case 'Right':
      button = 2;
      break;
    default:
      button = 1;
      break;
  }
  if (whitelistEnabled) {
    if (whitelistedSites.length > 0) {
      const whitelistedSitesArray = whitelistedSites.split(',').map((site) => site.trim());
      const currentSite = window.location.href;
      if (whitelistedSitesArray.includes(currentSite)) init();
    }
  } else {
    if (blacklistedSites.length > 0) {
      const blacklistedSitesArray = blacklistedSites.split(',').map((site) => site.trim());
      const currentSite = window.location.href;
      if (!blacklistedSitesArray.includes(currentSite)) init();
    } else init();
  }
}

function opencfg() {
  gmc.open();
  config.style = configcss;
}

const pauseEvent = (e) => {
  if (e.stopPropagation) e.stopPropagation();
  if (e.preventDefault) e.preventDefault();
  e.cancelBubble = true;
  e.returnValue = false;
  return false;
};

const getScrollValue = (value) => {
  return value * scrollSpeed;
};

const reset = () => {
  if (isMouseDown) {
    document.body.style.cursor = null;
  }
  isMouseDown = false;
  isScrolling = false;
  clearTimeout(startTimeout);
};

const checkScroll = (e) => {
  const rect = scrollDiv.getBoundingClientRect();
  const scrollDivX = rect.left + window.scrollX;
  clearTimeout(startTimeout);
  if (e.button === button && e.pageX >= scrollDivX && e.pageX <= scrollDivX + rect.width) {
    if (button !== 0) e.preventDefault();
    startTimeout = setTimeout(() => {
      isMouseDown = true;
      startY = e.clientY;
      startX = e.clientX;
      document.body.style.setProperty('cursor', 'grabbing');
    }, msClick);
  }
};

const scroll = (e) => {
  if (isMouseDown && e.buttons & (1 === 1)) pauseEvent(e);
  if (isScrolling || !isMouseDown) return;
  if (window.getSelection().toString().length > 0 || (textSelectable && Math.abs(startX - e.clientX) > 50)) {
    reset();
    return;
  }
  distance = -(startY - e.clientY);
  isScrolling = true;
  distance = getScrollValue(distance);
  animateScroll(distance);
};

function init() {
  scrollDiv.style.cssText = gmc.get('touchArea');
  document.body.appendChild(scrollDiv);
  document.addEventListener(
    'contextmenu',
    (e) => {
      if (button === 2 && !doubleClick) e.preventDefault();
    },
    false
  );
  document.addEventListener('mousedown', checkScroll);
  document.addEventListener('mouseup', reset);
  document.addEventListener('mousemove', scroll);
}