您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
微信读书阅读自动滚动, 触底自动翻页
// ==UserScript== // @name 微信读书自动阅读 // @name:en WeChatReadAutoScroll // @description 微信读书阅读自动滚动, 触底自动翻页 // @description:en 微信读书阅读自动滚动, 触底自动翻页 // @version 1.0.0 // @match https://weread.qq.com/web/reader/* // @author Yiero // @icon https://weread.qq.com/favicon.ico // @namespace https://github.com/AliubYiero/TamperMonkeyScripts/ // @license GPL // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_listValues // @run-at document-idle // ==/UserScript== var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value2) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value: value2 }) : obj[key] = value2; var __publicField = (obj, key, value2) => { __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value2); return value2; }; class Info { constructor(projectName) { __publicField(this, "projectName"); __publicField(this, "header"); this.projectName = projectName; this.header = `[${projectName}]`; } log(...msg) { (() => {})(...this.contentInfo(...msg)); } info(...msg) { console.info(...this.contentInfo(...msg)); } warn(...msg) { console.warn(...this.contentInfo(...msg)); } error(...msg) { console.error(...this.contentInfo(...msg)); } contentInfo(...msg) { return [ this.header, `[${(new Date).toLocaleString("zh-ch")}]`, ...msg ]; } } function MathCeilFloat(number, decimal = 0) { decimal = Math.pow(10, decimal); return Math.ceil(number * decimal) / decimal; } function isReachedPageBottom() { const {scrollTop: scrollTop, clientHeight: clientHeight, scrollHeight: scrollHeight} = document.documentElement; return scrollTop + clientHeight >= scrollHeight; } class ScrollOptionsImpl { constructor() { __publicField(this, "_delayPerMs", 0); __publicField(this, "_frameDuration", 0); __publicField(this, "_framePerSecond", 0); __publicField(this, "_movementDistancePerSecond", 0); } get delayPerMs() { return this._delayPerMs; } set delayPerMs(value) { if (typeof value === "string") { const scrollPageDuration = window.innerHeight / this.movementDistancePerSecond * 1e3; print.log(scrollPageDuration, this.movementDistancePerSecond); try { this._delayPerMs = eval(value.replace("auto", String(scrollPageDuration))); } catch (e) { this._delayPerMs = scrollPageDuration; } } else { this._delayPerMs = value; } } get movementDistancePerFrame() { return MathCeilFloat(this.movementDistancePerSecond / this.framePerSecond, 3); } get frameDuration() { return MathCeilFloat(1e3 / this._frameDuration); } get framePerSecond() { return this._framePerSecond; } set framePerSecond(value2) { this._framePerSecond = value2; } get movementDistancePerSecond() { return this._movementDistancePerSecond; } set movementDistancePerSecond(value2) { this._movementDistancePerSecond = value2; } } class GlobalAutoScroll { constructor(movementDistancePerSecond, delayPerMs = "auto", framePerSecond = 60) { __publicField(this, "options", new ScrollOptionsImpl); __publicField(this, "timer"); this.options.movementDistancePerSecond = movementDistancePerSecond; this.options.delayPerMs = delayPerMs; this.options.framePerSecond = framePerSecond; } open() { if (this.timer) { this.close(); } scrollStatusStorage.set("scrolling"); print.info("等待滚动: ", this.options.delayPerMs); this.timer = window.setTimeout((() => { this.timer = window.setInterval((() => { this.scroll(); if (isReachedPageBottom()) { this.close(); } }), this.options.frameDuration); }), this.options.delayPerMs); } close() { scrollStatusStorage.set("scroll-end"); clearTimeout(this.timer); clearInterval(this.timer); this.timer = void 0; } scroll() { const movementDistancePerFrame = this.options.movementDistancePerFrame; scrollBy(0, movementDistancePerFrame); } } class GMConfigMenu { constructor(callback) { __publicField(this, "menuId", 0); __publicField(this, "callback"); this.callback = callback; } open(title) { if (this.menuId) { this.close(); } this.menuId = GM_registerMenuCommand(title, this.callback); } close() { GM_unregisterMenuCommand(this.menuId); this.menuId = 0; } } class LocalStorage { constructor(key) { __publicField(this, "key"); this.key = key; } get(defaultValue) { return localStorage.getItem(this.key) || defaultValue || ""; } remove() { const oldValue = this.get(); const newValue = void 0; dispatchEvent(new CustomEvent("storageUpdate", { detail: { key: this.key, newValue: newValue, oldValue: oldValue } })); localStorage.removeItem(this.key); } set(value2) { const oldValue = this.get(); const newValue = value2; dispatchEvent(new CustomEvent("storageUpdate", { detail: { key: this.key, newValue: newValue, oldValue: oldValue } })); localStorage.setItem(this.key, value2); } } class SessionStorage { constructor(key) { __publicField(this, "key"); this.key = key; } get(defaultValue) { return sessionStorage.getItem(this.key) || defaultValue || ""; } remove() { const oldValue = this.get(); const newValue = void 0; dispatchEvent(new CustomEvent("storageUpdate", { detail: { key: this.key, newValue: newValue, oldValue: oldValue } })); sessionStorage.removeItem(this.key); } set(value2) { const oldValue = this.get(); const newValue = value2; dispatchEvent(new CustomEvent("storageUpdate", { detail: { key: this.key, newValue: newValue, oldValue: oldValue } })); sessionStorage.setItem(this.key, value2); } } new MouseEvent("mouseenter", { button: 0, bubbles: true, cancelable: true, clientX: 819, clientY: 413 }); new MouseEvent("mousedown", { bubbles: true, cancelable: true, button: 0 }); new MouseEvent("mouseup", { bubbles: true, cancelable: true, button: 0 }); new MouseEvent("click", { bubbles: true, cancelable: true, button: 0 }); function fireKeyEvent(el = document, evtType, keyCode) { let evtObj; if (document.createEvent) { if (window.KeyEvent) { evtObj = document.createEvent("KeyEvents"); evtObj.initKeyEvent(evtType, true, true); el.dispatchEvent(evtObj); return; } evtObj = document.createEvent("UIEvents"); evtObj.initUIEvent(evtType, true, true); delete evtObj.keyCode; if (typeof evtObj.keyCode === "undefined") { Object.defineProperty(evtObj, "keyCode", { value: keyCode }); } else { evtObj.key = String.fromCharCode(keyCode); } if (typeof evtObj.ctrlKey === "undefined") { Object.defineProperty(evtObj, "ctrlKey", { value: true }); } else { evtObj.ctrlKey = true; } el.dispatchEvent(evtObj); return; } } const print = new Info("WeChatReadAutoScroll"); const scrollStatusStorage = new SessionStorage("scrollStatus"); (async () => { const scrollSpeedController = new GMConfigMenu((() => { const result = prompt("页面滚动速度(px/s)"); if (!result) { return; } localStorage.setItem("scrollSpeed", result); })); scrollSpeedController.open("[配置] 滚动速度"); const scrollDelayController = new GMConfigMenu((() => { const result = prompt("滚动开启延时 ( auto | (auto / 2) | number ) (ms)"); if (!result) { return; } localStorage.setItem("scrollDelay", result); })); scrollDelayController.open("[配置] 滚动开启延时"); function scrollSwitch() { let globalAutoScroll; const scrollOpenController = new GMConfigMenu(scrollOpenMenuCallback); function scrollOpenMenuCallback() { const scrollSpeedStorage = new LocalStorage("scrollSpeed"); const scrollDelayStorage = new LocalStorage("scrollDelay"); globalAutoScroll = new GlobalAutoScroll(+scrollSpeedStorage.get("100"), scrollDelayStorage.get("auto"), 60); globalAutoScroll.open(); window.addEventListener("click", (() => { globalAutoScroll.close(); }), { once: true }); scrollOpenController.close(); scrollCloseController.open("关闭滚动"); } const scrollCloseController = new GMConfigMenu((() => { globalAutoScroll.close(); scrollCloseMenuCallback(); })); function scrollCloseMenuCallback() { scrollCloseController.close(); scrollOpenController.open("开启滚动"); } scrollOpenController.open("开启滚动"); scrollStatusStorage.set("scroll-start"); window.addEventListener("storageUpdate", (e => { const event = e; if (event.detail.key === "scrollStatus" && event.detail.newValue === "scroll-end") { scrollCloseMenuCallback(); if (isReachedPageBottom()) { fireKeyEvent(document, "keydown", 39); scrollOpenMenuCallback(); } } })); } scrollSwitch(); })();