Touch Scroll Speed Multiplier

Increases the touchscreen scroll speed in most websites.

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==UserScript==
// @name         Touch Scroll Speed Multiplier
// @namespace    http://tampermonkey.net/
// @version      1.4
// @license      MIT
// @description  Increases the touchscreen scroll speed in most websites.
// @match        *://*/*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    const MULTIPLIER = 2.5;
    const START_THRESHOLD = 6;

    const FAIL_RATIO = 0.35;      // poniżej tego uznajemy, że scroll nie działa sensownie
    const FAIL_STREAK_LIMIT = 2;  // po tylu porażkach wyłączamy skrypt na stronie
    const STORAGE_KEY = 'tssm_disabled:' + location.hostname;

    let lastY = null;
    let isHandling = false;
    let movedEnough = false;

    let pageDisabled = false;
    let failureStreak = 0;
    let probeDone = false;

    try {
        if (sessionStorage.getItem(STORAGE_KEY) === '1') {
            pageDisabled = true;
        }
    } catch (_) {}

    function disableForThisPage() {
        pageDisabled = true;
        try {
            sessionStorage.setItem(STORAGE_KEY, '1');
        } catch (_) {}
    }

    function getRoot() {
        return document.scrollingElement || document.documentElement;
    }

    function isScrollable(el) {
        if (!(el instanceof Element)) return false;

        const style = getComputedStyle(el);
        const overflowY = style.overflowY;

        return /(auto|scroll|overlay)/.test(overflowY) && el.scrollHeight > el.clientHeight + 1;
    }

    function canScroll(el, delta) {
        const maxScrollTop = el.scrollHeight - el.clientHeight;
        if (maxScrollTop <= 0) return false;

        if (delta > 0) return el.scrollTop < maxScrollTop - 1;
        if (delta < 0) return el.scrollTop > 1;

        return false;
    }

    function getParent(el) {
        if (!el) return null;
        return el.parentElement || (el.getRootNode && el.getRootNode().host) || null;
    }

    function findScrollTarget(x, y, delta) {
        let el = document.elementFromPoint(x, y);

        while (el && el !== document.body && el !== document.documentElement) {
            if (isScrollable(el) && canScroll(el, delta)) {
                return el;
            }
            el = getParent(el);
        }

        const root = getRoot();
        return canScroll(root, delta) ? root : null;
    }

    function getScrollPos(target) {
        const root = getRoot();
        if (target === root) {
            return window.scrollY || window.pageYOffset || 0;
        }
        return target.scrollTop;
    }

    function probeRootScrollOnce() {
        if (probeDone || pageDisabled) return;
        probeDone = true;

        const root = getRoot();
        const maxScrollTop = root.scrollHeight - root.clientHeight;

        if (root.scrollHeight <= root.clientHeight + 5) return;

        const before = getScrollPos(root);
        const step = before < maxScrollTop - 1 ? 1 : -1;
        const testValue = before + step;

        root.scrollTop = testValue;

        const after = getScrollPos(root);
        const works = Math.abs(after - testValue) < 0.5;

        root.scrollTop = before;

        if (!works) {
            disableForThisPage();
        }
    }

    function recordScrollResult(target, before, amount) {
        requestAnimationFrame(() => {
            if (pageDisabled) return;

            const after = getScrollPos(target);
            const moved = Math.abs(after - before);
            const expected = Math.max(1, Math.abs(amount) * FAIL_RATIO);

            if (moved < expected) {
                failureStreak++;
                if (failureStreak >= FAIL_STREAK_LIMIT) {
                    disableForThisPage();
                }
            } else {
                failureStreak = 0;
            }
        });
    }

    function scrollTarget(target, amount) {
        const before = getScrollPos(target);
        const root = getRoot();

        if (target === root) {
            window.scrollBy(0, amount);
        } else if (typeof target.scrollBy === 'function') {
            target.scrollBy(0, amount);
        } else {
            target.scrollTop = before + amount;
        }

        recordScrollResult(target, before, amount);
    }

    function onTouchStart(e) {
        if (pageDisabled) return;

        probeRootScrollOnce();

        if (e.touches && e.touches.length === 1) {
            isHandling = true;
            movedEnough = false;
            lastY = e.touches[0].clientY;
        } else {
            isHandling = false;
            lastY = null;
        }
    }

    function onTouchMove(e) {
        if (pageDisabled) return;

        if (!isHandling || !e.touches || e.touches.length !== 1) {
            isHandling = false;
            movedEnough = false;
            return;
        }

        const touch = e.touches[0];
        const y = touch.clientY;
        const delta = lastY - y;
        lastY = y;

        if (!movedEnough && Math.abs(delta) < START_THRESHOLD) {
            return;
        }

        movedEnough = true;

        const target = findScrollTarget(touch.clientX, touch.clientY, delta);
        if (!target) return;

        e.preventDefault();
        scrollTarget(target, delta * MULTIPLIER);
    }

    function onTouchEnd() {
        isHandling = false;
        movedEnough = false;
        lastY = null;
    }

    document.addEventListener('touchstart', onTouchStart, { passive: true });
    document.addEventListener('touchmove', onTouchMove, { passive: false });
    document.addEventListener('touchend', onTouchEnd, { passive: true });
    document.addEventListener('touchcancel', onTouchEnd, { passive: true });
})();