PageUp and PageDown buttons

PageUp and PageDown buttons on the side of each page. Hold to scroll to top/bottom. Mainly useful for reading web pages on e-ink devices, but can be handy for other touch devices.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         PageUp and PageDown buttons
// @namespace    http://tampermonkey.net/
// @version      0.2.4
// @description  PageUp and PageDown buttons on the side of each page. Hold to scroll to top/bottom. Mainly useful for reading web pages on e-ink devices, but can be handy for other touch devices.
// @author       xiaq
// @match        *://*/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=tampermonkey.net
// @grant        none
// @license      BSD 2-clause
// ==/UserScript==

(function() {
    'use strict';

    const scrollFactor = 0.8; // How much of the page PageUp/PageDown scrolls by
    const opacity = 0.5; // Opacity of all buttons. 0 = transparent, 1 = opaque
    const right = true; // true = right side, false = left side
    const holdMs = 1000; // how long to hold to trigger scrolling to top/bottom, in milliseconds
    const sideGapPx = 8; // Gap between each button with the left/right side of the page
    const buttonsGapPx = 12; // Gap between PageUp and PageDown buttons
    const boxSizePx = 48;
    const fontSizePx = 32;

    const commonStyle = `
        opacity: ${opacity};
        width: ${boxSizePx}px;
        height: ${boxSizePx}px;
        font-size: ${fontSizePx}px;
        border: 1px solid black;
        display: flex;
        align-items: center;
        justify-content: center;
        position: fixed;
        ${right? 'right' : 'left'}: ${sideGapPx}px;
    `;

    const distance = `calc(50% + ${buttonsGapPx / 2}px)`;
    // PageUp
    addButton('▲', 'bottom:' + distance,
              () => { window.scrollBy(0, -scrollFactor * document.documentElement.clientHeight) },
              () => { window.scrollTo(0, 0); });
    // PageDown
    addButton('▼', 'top:' + distance,
              () => { window.scrollBy(0, scrollFactor * document.documentElement.clientHeight) },
              () => { window.scrollTo(0, document.body.scrollHeight); });

    function addButton(text, style, press, hold) {
        const button = document.createElement('button');
        button.innerText = text;
        button.style = commonStyle + style;
        button.onclick = press;

        let holdTimeout;
        const start = () => {
            holdTimeout = setTimeout(() => {
                hold();
                holdTimeout = undefined;
            }, holdMs);
        }
        const cancel = () => {
            if (holdTimeout) {
                clearTimeout(holdTimeout);
                holdTimeout = undefined;
            }
        };
        button.onmousedown = start;
        button.onmouseup = cancel;
        button.onmouseleave = cancel;

        button.ontouchstart = start;
        button.ontouchend = cancel;
        button.ontouchcancel = cancel;

        document.body.appendChild(button);
    }
})();