// ==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
// ==/UserScript==
(function(){
// --- 设置项 ---
let mx = GM_getValue('multiplierX', 5),
my = GM_getValue('multiplierY', 2),
intervalSec = GM_getValue('interval', 0),
enableZoom = GM_getValue('enableZoom', 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(`切换双指放大/双击复位(当前 ${enableZoom?'开':'关'})`, ()=>{
GM_setValue('enableZoom', enableZoom=!enableZoom);
location.reload();
});
// --- 缩放函数 ---
const defaultScale = 1;
let currentScale = defaultScale, pinch = null;
function applyScale(s){
const html = document.documentElement;
html.style.transformOrigin = '0 0';
html.style.transform = 'scale('+s+')';
html.style.width = (100/s)+'vw';
}
// --- 滑动逻辑 ---
const touchMap = {};
let timerId = null;
function periodicUpdate(){
for(let id in touchMap){
let info = touchMap[id];
if(info.cx==null||info.cy==null) continue;
let dxTot = (info.sx - info.cx)*mx,
dyTot = (info.sy - info.cy)*my,
dX = dxTot - (info.lastDx||0),
dY = dyTot - (info.lastDy||0);
if(dX) info.el.scrollLeft += dX;
if(dY) info.el.scrollTop += dY;
info.lastDx = dxTot;
info.lastDy = dyTot;
}
}
function startTimer(){
if(intervalSec>0 && !timerId)
timerId = setInterval(periodicUpdate, intervalSec*1000);
}
function stopTimer(){
if(timerId){ clearInterval(timerId); timerId = null; }
}
document.addEventListener('touchstart', e=>{
// 双指缩放
if(enableZoom && e.touches.length===2){
let [t1,t2] = e.touches;
pinch = {
initialDist: Math.hypot(t2.clientX-t1.clientX, t2.clientY-t1.clientY),
initialScale: currentScale
};
e.preventDefault();
return;
}
if(e.touches.length>1) return;
for(let t of e.changedTouches){
let el = t.target;
while(el && el!==document){
let st = getComputedStyle(el),
canY = el.scrollHeight>el.clientHeight && /auto|scroll/.test(st.overflowY),
canX = el.scrollWidth>el.clientWidth && /auto|scroll/.test(st.overflowX);
if(canY||canX) 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=>{
// pinch 缩放
if(pinch){
let [t1,t2] = e.touches;
let dist = Math.hypot(t2.clientX-t1.clientX, t2.clientY-t1.clientY);
currentScale = +(pinch.initialScale * dist/pinch.initialDist).toFixed(2);
applyScale(currentScale);
e.preventDefault();
return;
}
if(e.touches.length>1) return;
let doPrevent = false;
for(let t of e.changedTouches){
let 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){
// pinch 结束
if(pinch && e.touches.length<2){
pinch = null;
return;
}
if(e.touches.length>1) return;
for(let t of e.changedTouches){
let info = touchMap[t.identifier];
if(!info) continue;
if(intervalSec===0){
let dx=(info.sx-t.clientX)*mx,
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) stopTimer();
}
document.addEventListener('touchend', finishTouch, { passive:false });
document.addEventListener('touchcancel', finishTouch,{ passive:false });
// 双击复位
let lastTap=0;
document.addEventListener('touchend', e=>{
if(!enableZoom) return;
if(e.touches.length===0 && e.changedTouches.length===1){
let now = Date.now();
if(now - lastTap < 300){
let de = document.scrollingElement||document.documentElement,
lx = de.scrollLeft, ly = de.scrollTop;
currentScale = defaultScale;
applyScale(currentScale);
de.scrollLeft = lx;
de.scrollTop = ly;
}
lastTap = now;
}
}, { passive:false });
// 页面载入时应用默认缩放
if(enableZoom) applyScale(defaultScale);
})();