NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Hotkeys Page Scroll // @namespace hotkey-scroll-page // @version 0.1 // @description Scroll half a page up and down when pressing the hotkeykeys // @author raingart // @license Apache-2.0 // @include *://*/* // @run-at document-end // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @grant GM_registerMenuCommand // @require https://cdn.jsdelivr.net/gh/kufii/My-UserScripts@c7f613292672252995cb02a0cab3b6acb18ccac5/libs/gm_config.js // ==/UserScript== /*jshint esversion: 6 */ (function() { const keysList = ['none','KeyA','KeyB','KeyC','KeyD','KeyE','KeyF','KeyG','KeyH','KeyI','KeyJ','KeyK','KeyL','KeyM','KeyN','KeyO','KeyP','KeyQ','KeyR','KeyS','KeyT','KeyU','KeyV','KeyW','KeyX','KeyY','KeyZ','[',']','+','-',',','.','/','<',';','\\','ArrowUp','ArrowDown','ArrowLeft','ArrowRight','ShiftLeft','ShiftRight','ControlLeft','ControlRight','AltLeft','AltRight',0,1,2,3,4,5,6,7,8,9]; const config = GM_config([ { key: 'scroll-up', label: 'Scroll up', default: 'KeyW', type: 'dropdown', values: keysList, }, { key: 'scroll-down', label: 'Scroll down', default: 'KeyS', type: 'dropdown', values: keysList, }, { key: 'scroll-half-up', label: 'Scroll-half up', default: 'KeyE', type: 'dropdown', values: keysList, }, { key: 'scroll-half-down', label: 'Scroll-half down', default: 'KeyD', type: 'dropdown', values: keysList, }, { key: 'scroll-duration', label: 'Default scroll duration (ms)', default: 150, min: 50, max: 500, step: 10, type: 'number', }, { key: 'scroll-distance', label: 'Default scroll distance (px)', default: 30, min: 5, max: 100, step: 1, type: 'number', }, ]); const conf = config.load(); config.onsave = cfg => (conf = cfg); GM_registerMenuCommand('Options', config.setup); document.addEventListener('keydown', evt => { // Check for modifier keys being pressed if (evt.ctrlKey || evt.altKey || evt.metaKey || evt.shiftKey) return; if (['input', 'textarea', 'select'].includes(evt.target.localName) || evt.target.isContentEditable) return; const targetScrollPosition = determineTargetScrollPosition(evt.code) if (+targetScrollPosition) { evt.preventDefault(); evt.stopPropagation(); evt.stopImmediatePropagation(); startScrolling(targetScrollPosition); } function startScrolling(targetScrollPosition) { let startTime; let scrollInterval = requestAnimationFrame(smoothScroll); function smoothScroll(currentTime = performance.now()) { if (evt.repeat) cancelAnimationFrame(scrollInterval); if (!startTime) startTime = currentTime; const scrolledProgress = Math.min((currentTime - startTime) / +conf['scroll-duration'], 1); const distance = (targetScrollPosition - window.pageYOffset) * scrolledProgress; scrollApply({ 'distance': distance }); if (scrolledProgress < 1) requestAnimationFrame(smoothScroll); else cancelAnimationFrame(scrollInterval); } function scrollApply({ distance, direction = 'vertical' }) { if (direction !== 'vertical' && direction !== 'horizontal') { throw new Error('Invalid scroll direction. Use "vertical" or "horizontal".'); } switch (direction) { case 'vertical': window.scrollBy(0, distance); break; case 'horizontal': window.scrollBy(distance, 0); break; } } } function determineTargetScrollPosition(keyCode) { let targetScrollPosition = 0; switch (keyCode) { case conf['scroll-up']: targetScrollPosition = window.pageYOffset - +conf['scroll-distance']; break; case conf['scroll-down']: targetScrollPosition = window.pageYOffset + +conf['scroll-distance']; break; case conf['scroll-half-up']: targetScrollPosition = window.pageYOffset - window.innerHeight / 2; break; case conf['scroll-half-down']: targetScrollPosition = Math.min(window.pageYOffset + window.innerHeight / 2, document.body.scrollHeight); break; // Add cases for other key presses if needed } return targetScrollPosition; } }, { capture: true }); })();