您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
消除划动动画 可设置划动倍率和更新间隔 支持手势缩放和双击复位
当前为
// ==UserScript== // @name 墨水屏电纸书划动优化 // @namespace cc.cxuan.books // @version 1.28 // @description 消除划动动画 可设置划动倍率和更新间隔 支持手势缩放和双击复位 // @match *://*/* // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @run-at document-end // @noframes // @license MIT // @author cxuan.cc // ==/UserScript== (function(){ // --- 设置项 --- let mx = GM_getValue('multiplierX', 5); let my = GM_getValue('multiplierY', 2); let intervalSec = GM_getValue('interval', 0); // 控制双击复位 & 两指缩放 let enableGesture = GM_getValue('enableGesture', true); GM_registerMenuCommand(`设置X轴移动倍率(当前 ${mx})`, ()=>{ let v = parseFloat(prompt('X 轴滑动倍率:', mx)); if (!isNaN(v)) { GM_setValue('multiplierX', mx = v); location.reload(); } }); GM_registerMenuCommand(`设置Y轴移动倍率(当前 ${my})`, ()=>{ let v = parseFloat(prompt('Y 轴滑动倍率:', my)); if (!isNaN(v)) { GM_setValue('multiplierY', my = v); location.reload(); } }); GM_registerMenuCommand(`设置更新间隔(当前 ${intervalSec}s)`, ()=>{ let v = parseFloat(prompt('更新间隔(秒,0=仅松手时刷新):', intervalSec)); if (!isNaN(v)) { GM_setValue('interval', intervalSec = v); location.reload(); } }); GM_registerMenuCommand(`切换手势缩放/双击复位(当前 ${enableGesture?'开':'关'})`, ()=>{ enableGesture = !enableGesture; GM_setValue('enableGesture', enableGesture); location.reload(); }); const initialZoom = parseFloat(document.body.style.zoom) || 1; let currentZoom = initialZoom; let pinch = null; // {startDist, startZoom} const touchMap = {}; // 单指滑动数据 let timerId = null; function periodicUpdate() { for (let id in touchMap) { const info = touchMap[id]; if (info.cx==null||info.cy==null) continue; let totalDx = (info.sx - info.cx) * mx; let totalDy = (info.sy - info.cy) * my; let dX = totalDx - (info.lastDx||0); let dY = totalDy - (info.lastDy||0); if (dX) info.el.scrollLeft += dX; if (dY) info.el.scrollTop += dY; info.lastDx = totalDx; info.lastDy = totalDy; } } function startTimer(){ if(intervalSec>0 && !timerId){ timerId = setInterval(periodicUpdate, intervalSec*1000); } } function stopTimer(){ if(timerId){ clearInterval(timerId); timerId = null; } } document.addEventListener('touchstart', e=>{ if (enableGesture && e.touches.length===2) { // 两指缩放 e.preventDefault(); stopTimer(); let [t1, t2] = e.touches; let d0 = Math.hypot(t1.clientX - t2.clientX, t1.clientY - t2.clientY); pinch = { startDist: d0, startZoom: currentZoom }; return; } if (e.touches.length>1) return; for (const t of e.changedTouches) { let el = t.target; while(el && el!==document){ const style = getComputedStyle(el); const canScrollY = el.scrollHeight>el.clientHeight && /auto|scroll/.test(style.overflowY); const canScrollX = el.scrollWidth>el.clientWidth && /auto|scroll/.test(style.overflowX); if (canScrollY||canScrollX) break; el = el.parentNode; } if (!el||el===document){ el = document.scrollingElement||document.documentElement; } touchMap[t.identifier] = { sx: t.clientX, sy: t.clientY, cx: t.clientX, cy: t.clientY, el, lastDx:0, lastDy:0 }; } startTimer(); }, {passive:false}); document.addEventListener('touchmove', e=>{ if (pinch && e.touches.length===2) { // pinch缩放中 e.preventDefault(); let [t1, t2] = e.touches; let d = Math.hypot(t1.clientX - t2.clientX, t1.clientY - t2.clientY); currentZoom = pinch.startZoom * (d / pinch.startDist); document.body.style.zoom = currentZoom; return; } if (e.touches.length>1) return; let doPrevent = false; for (const t of e.changedTouches) { const info = touchMap[t.identifier]; if (!info) continue; info.cx = t.clientX; info.cy = t.clientY; // 下拉刷新放行 if (!(info.el === (document.scrollingElement||document.documentElement) && info.el.scrollTop===0 && (t.clientY-info.sy)>0)) { doPrevent = true; } } if (doPrevent) e.preventDefault(); }, {passive:false}); function finishTouch(e) { if (pinch) { pinch = null; return; } if (e.touches.length>1) return; for (const t of e.changedTouches) { const info = touchMap[t.identifier]; if (!info) continue; if (intervalSec===0) { let dx = (info.sx - t.clientX)*mx; let dy = (info.sy - t.clientY)*my; info.el.scrollLeft += dx; info.el.scrollTop += dy; } else { info.cx = t.clientX; info.cy = t.clientY; periodicUpdate(); } delete touchMap[t.identifier]; } if (Object.keys(touchMap).length===0) stopTimer(); } document.addEventListener('touchend', finishTouch, {passive:false}); document.addEventListener('touchcancel', finishTouch, {passive:false}); // 双击复位到 initialZoom let lastTap = 0; document.addEventListener('touchend', e=>{ if (!enableGesture) return; if (e.touches.length===0 && e.changedTouches.length===1) { let now = Date.now(); if (now - lastTap < 300) { let docEl = document.scrollingElement||document.documentElement; let lx = docEl.scrollLeft, ly = docEl.scrollTop; document.body.style.zoom = initialZoom; currentZoom = initialZoom; docEl.scrollLeft = lx; docEl.scrollTop = ly; } lastTap = now; } }, {passive:true}); })();