// ==UserScript==
// @name 📘极速翻页丨全端可用丨无动画全屏切换丨自定义悬浮窗丨黑色模式
// @version 2.1
// @description 👍忽略滑动动画,快速切换页面,可以通过键盘或浮动窗口进行操作,并调整黑色模式、悬浮窗大小和滑动保留页面比例。
// @author Jingyu0123
// @match *://*/*
// @license GPL
// @namespace https://greasyfork.org/users/1292046
// ==/UserScript==
(function () {
"use strict";
// 默认配置变量
const autoDetectModeOptions = ["关闭", "同色", "反色"];
var usePgUpPgDn = GM_getValue("usePgUpPgDn", true);
var reservedHeightPercentage = GM_getValue("reservedHeightPercentage", 0);
var isBlackMode = GM_getValue("isBlackMode", false);
var floatWindowSize = GM_getValue("floatWindowSize", 80);
var autoDetectMode = GM_getValue("autoDetectMode", "关闭");
var isVerticalLayout = GM_getValue("isVerticalLayout", true);
var windowOpacity = GM_getValue("windowOpacity", 0.8);
var textOpacity = GM_getValue("textOpacity", 1.0);
var floatWindowVisible = GM_getValue("floatWindowVisible", true);
var upKey = GM_getValue("upKey", "PageUp");
var downKey = GM_getValue("downKey", "PageDown");
var enableHotkeys = GM_getValue("enableHotkeys", true);
// 自动检测黑色模式
function detectDarkMode() {
const bgColor = window.getComputedStyle(document.body).backgroundColor;
const colorValues = bgColor.match(/\d+/g);
if (colorValues) {
const r = parseInt(colorValues[0]);
const g = parseInt(colorValues[1]);
const b = parseInt(colorValues[2]);
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
return brightness < 128;
}
return false;
}
if (autoDetectMode === "同色") {
isBlackMode = detectDarkMode();
} else if (autoDetectMode === "反色") {
isBlackMode = !detectDarkMode();
}
// 创建悬浮窗
function createFloatingWindow() {
// 检查是否已经存在浮窗
if (document.getElementById("floatingWindow")) {
return; // 如果已经存在浮窗,则不创建新的浮窗
}
if (!floatWindowVisible) return;
const floatWindow = document.createElement("div");
floatWindow.id = "floatingWindow"; // 为浮窗设置一个唯一的ID
const initialLeft = GM_getValue("floatWindowLeft", "calc(100% - 90px)");
const initialTop = GM_getValue("floatWindowTop", "calc(100% - 270px)");
floatWindow.style = `
position: fixed;
left: ${initialLeft};
top: ${initialTop};
width: ${isVerticalLayout ? floatWindowSize + "px" : floatWindowSize * 3 + "px"};
height: ${isVerticalLayout ? floatWindowSize * 3 + "px" : floatWindowSize + "px"};
background-color: ${isBlackMode ? "black" : "white"};
color: ${isBlackMode ? "white" : "black"};
border: 1px solid #ccc;
z-index: 10000;
display: flex;
flex-direction: ${isVerticalLayout ? "column" : "row"};
align-items: center;
justify-content: space-around;
cursor: move;
padding: 5px;
border-radius: 5px;
opacity: ${windowOpacity};
`;
floatWindow.draggable = true;
const btnSettings = document.createElement("button");
btnSettings.innerHTML = "=";
btnSettings.style = buttonStyle();
btnSettings.onclick = showSettings;
const btnDown = document.createElement("button");
btnDown.innerHTML = "↓";
btnDown.style = buttonStyle();
btnDown.onclick = () => scrollPage(1);
const btnUp = document.createElement("button");
btnUp.innerHTML = "↑";
btnUp.style = buttonStyle();
btnUp.onclick = () => scrollPage(-1);
floatWindow.appendChild(btnSettings);
floatWindow.appendChild(btnUp);
floatWindow.appendChild(btnDown);
document.body.appendChild(floatWindow);
// 悬浮窗拖拽功能
floatWindow.addEventListener(
"dragstart",
(event) => {
const style = window.getComputedStyle(event.target, null);
event.dataTransfer.setData(
"text/plain",
parseInt(style.getPropertyValue("left"), 10) -
event.clientX +
"," +
(parseInt(style.getPropertyValue("top"), 10) - event.clientY),
);
},
false,
);
document.body.addEventListener(
"dragover",
(event) => {
event.preventDefault();
return false;
},
false,
);
document.body.addEventListener(
"drop",
(event) => {
const offset = event.dataTransfer.getData("text/plain").split(",");
const floatWindow = document.querySelector('div[draggable="true"]');
const left = event.clientX + parseInt(offset[0], 10) + "px";
const top = event.clientY + parseInt(offset[1], 10) + "px";
floatWindow.style.left = left;
floatWindow.style.top = top;
GM_setValue("floatWindowLeft", left);
GM_setValue("floatWindowTop", top);
event.preventDefault();
return false;
},
false,
);
}
// 滚动页面函数
function scrollPage(direction) {
const screenHeight = window.innerHeight;
const reservedHeight = (screenHeight * reservedHeightPercentage) / 100;
window.scrollBy(0, direction * (screenHeight - reservedHeight));
}
// 键盘事件监听
window.addEventListener("keydown", function (event) {
if (!enableHotkeys) return; // 检查是否启用热键
const screenHeight = window.innerHeight;
const reservedHeight = (screenHeight * reservedHeightPercentage) / 100;
if (event.key === upKey || event.key === downKey) {
const direction = event.key === downKey ? 1 : -1;
window.scrollBy(0, direction * (screenHeight - reservedHeight));
event.preventDefault();
}
});
// 显示设置窗口
function showSettings() {
const settingsWindow = document.createElement("div");
settingsWindow.style = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 300px;
background-color: ${isBlackMode ? "black" : "white"};
color: ${isBlackMode ? "white" : "black"};
border: 1px solid #ccc;
z-index: 10001;
padding: 20px;
border-radius: 5px;
`;
const settingsContainer = document.createElement("div");
settingsContainer.style = `
display: flex;
flex-direction: column;
align-items: flex-start;
color: ${isBlackMode ? "white" : "black"};
`;
// 更新颜色
const updateColors = () => {
settingsWindow.style.backgroundColor = isBlackMode ? "black" : "white";
settingsWindow.style.color = isBlackMode ? "white" : "black";
settingsContainer.style.color = isBlackMode ? "white" : "black";
saveButton.style.backgroundColor = isBlackMode ? "black" : "white";
saveButton.style.color = isBlackMode ? "white" : "black";
donateButton.style.backgroundColor = isBlackMode ? "black" : "white";
donateButton.style.color = isBlackMode ? "white" : "black";
};
// 样式设置
const createSetting = (labelText, value, onClick) => {
const container = document.createElement("div");
container.style = `
display: flex;
justify-content: space-between;
width: 100%;
margin-bottom: 10px;
cursor: pointer;
`;
container.onclick = onClick;
const label = document.createElement("span");
label.innerText = labelText;
container.appendChild(label);
const indicator = document.createElement("span");
indicator.innerText = value ? "✔️" : "✖️";
container.appendChild(indicator);
return container;
};
const modeSetting = createSetting("🌑黑色模式", isBlackMode, () => {
isBlackMode = !isBlackMode;
modeSetting.lastChild.innerText = isBlackMode ? "✔️" : "✖️";
GM_setValue("isBlackMode", isBlackMode);
updateColors();
});
const createAutoDetectSetting = (labelText, currentOption, optionValue) => {
const container = document.createElement("div");
container.style = `
display: flex;
justify-content: space-between;
align-items: center;
flex: 1;
margin-bottom: 10px;
cursor: pointer;
padding-right: 10px;
`;
container.onclick = () => {
autoDetectMode = optionValue;
GM_setValue("autoDetectMode", autoDetectMode);
updateAutoDetectSettings();
};
const label = document.createElement("span");
label.innerText = labelText;
container.appendChild(label);
const indicator = document.createElement("span");
indicator.innerText = currentOption === optionValue ? "✔️" : "✖️";
container.appendChild(indicator);
return container;
};
const updateAutoDetectSettings = () => {
autoDetectSettingContainer.style = `
display: flex;
flex-direction: column;
align-items: flex-start;
width: 100%;
`;
autoDetectSettingContainer.innerHTML = "";
// 创建并定义解释文字的DOM元素
const explanationText = document.createElement("div");
explanationText.innerText = "🔎自动调整悬浮窗颜色(忽略上方修改)";
explanationText.style =
"margin-bottom: 10px; display: block; width: 100%; text-align: left;";
autoDetectSettingContainer.appendChild(explanationText);
// 创建自动检测设置按钮
const settingsContainer = document.createElement("div");
settingsContainer.style =
"display: flex; justify-content: space-between; width: 80%;";
settingsContainer.appendChild(
createAutoDetectSetting("关闭", autoDetectMode, "关闭"),
);
settingsContainer.appendChild(
createAutoDetectSetting("同色", autoDetectMode, "同色"),
);
settingsContainer.appendChild(
createAutoDetectSetting("反色", autoDetectMode, "反色"),
);
autoDetectSettingContainer.appendChild(settingsContainer);
};
const layoutSetting = createSetting(
"↕️竖向排列按钮",
isVerticalLayout,
() => {
isVerticalLayout = !isVerticalLayout;
layoutSetting.lastChild.innerText = isVerticalLayout ? "✔️" : "✖️";
},
);
const createPercentageInput = (labelText, value, min, max) => {
const container = document.createElement("div");
container.style = `
display: flex;
justify-content: space-between;
width: 100%;
margin-bottom: 10px;
`;
const label = document.createElement("label");
label.innerText = labelText;
container.appendChild(label);
const input = document.createElement("input");
input.type = "number";
input.value = value;
input.min = min;
input.max = max;
input.style = `
width: 50px;
background-color: ${isBlackMode ? "black" : "white"};
color: ${isBlackMode ? "white" : "black"};
`;
const percentageLabel = document.createElement("span");
percentageLabel.innerText = "%";
container.appendChild(input);
container.appendChild(percentageLabel);
return { container, input };
};
const sizeInput = createPercentageInput(
"📏悬浮窗大小",
floatWindowSize,
20,
100,
);
const percentageInput = createPercentageInput(
"📄保留页面比例",
reservedHeightPercentage,
0,
100,
);
const windowOpacityInput = createPercentageInput(
"🌫️窗口透明度",
windowOpacity * 100,
20,
100,
);
const textOpacityInput = createPercentageInput(
"🖋️文字透明度",
textOpacity * 100,
20,
100,
);
const saveButton = document.createElement("button");
saveButton.innerHTML = "💾保存";
saveButton.style = `
margin-top: 10px;
background-color: ${isBlackMode ? "black" : "white"};
color: ${isBlackMode ? "white" : "black"};
`;
saveButton.onclick = () => {
floatWindowSize = parseInt(sizeInput.input.value, 10);
reservedHeightPercentage = Math.min(
100,
Math.max(0, parseInt(percentageInput.input.value, 10)),
);
windowOpacity = Math.min(
1,
Math.max(0.2, parseFloat(windowOpacityInput.input.value) / 100),
);
textOpacity = Math.min(
1,
Math.max(0.2, parseFloat(textOpacityInput.input.value) / 100),
);
GM_setValue("floatWindowSize", floatWindowSize);
GM_setValue("isVerticalLayout", isVerticalLayout);
GM_setValue("reservedHeightPercentage", reservedHeightPercentage);
GM_setValue("windowOpacity", windowOpacity);
GM_setValue("textOpacity", textOpacity);
document.body.removeChild(settingsWindow);
location.reload();
};
const donateButton = document.createElement("button");
donateButton.innerHTML = "🔄更新/💰打赏";
donateButton.style = `
margin-top: 10px;
background-color: ${isBlackMode ? "black" : "white"};
color: ${isBlackMode ? "white" : "black"};
`;
donateButton.onclick = () => {
if (
confirm(
"⚠️一部分脚本管理器无法自动检测脚本是否有更新,所以需要你去看一下。\n🔄现在的版本号是:v2.1。\n💬如果脚本有任何问题,或者你有任何创意,都可以在脚本反馈区留言。\n📄在脚本发布页的介绍部分也放置了我的打赏二维码,\n🥤如果这个脚本帮你解决了大问题,希望你能请我喝杯奶茶哦!",
)
) {
window.open("https://greasyfork.org/zh-CN/scripts/493257", "_blank");
}
};
const toggleFloatWindow = () => {
floatWindowVisible = !floatWindowVisible;
GM_setValue("floatWindowVisible", floatWindowVisible);
document.body.removeChild(settingsWindow);
location.reload();
};
const confirmToggleFloatWindow = () => {
if (
confirm(
"❗️确定要关闭悬浮窗吗?\n🖥️电脑端可以通过脚本管理器中的选项重新打开,\n📱若脚本管理器没有图形化界面,可以重新安装脚本。",
)
) {
toggleFloatWindow();
}
};
const floatWindowSetting = createSetting(
"📴关闭悬浮窗(长按悬浮窗可以移动)",
false,
confirmToggleFloatWindow,
);
// 分割线
const createSeparator = () => {
const separator = document.createElement("hr");
separator.style =
"width: 100%; margin: 10px 0; border: none; border-top: 1px solid #ccc;";
return separator;
};
settingsContainer.appendChild(modeSetting);
const hotkeysToggleSetting = createSetting(
"开启翻页热键功能",
enableHotkeys,
() => {
enableHotkeys = !enableHotkeys;
hotkeysToggleSetting.lastChild.innerText = enableHotkeys ? "✔️" : "✖️";
GM_setValue("enableHotkeys", enableHotkeys);
},
);
const autoDetectSettingContainer = document.createElement("div");
settingsContainer.appendChild(autoDetectSettingContainer);
updateAutoDetectSettings();
settingsContainer.appendChild(createSeparator());
settingsContainer.appendChild(layoutSetting);
settingsContainer.appendChild(sizeInput.container);
settingsContainer.appendChild(percentageInput.container);
settingsContainer.appendChild(windowOpacityInput.container);
settingsContainer.appendChild(textOpacityInput.container);
settingsContainer.appendChild(floatWindowSetting);
settingsContainer.appendChild(createSeparator());
const hotkeySetting = createSetting("🎛️设置电脑端翻页热键", false, () => {
const hotkeyWindow = document.createElement("div");
hotkeyWindow.style = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 300px;
background-color: ${isBlackMode ? "black" : "white"};
color: ${isBlackMode ? "white" : "black"};
border: 1px solid #ccc;
z-index: 10002;
padding: 20px;
border-radius: 5px;
`;
const hotkeyContainer = document.createElement("div");
hotkeyContainer.style = `
display: flex;
flex-direction: column;
align-items: flex-start;
color: ${isBlackMode ? "white" : "black"};
`;
// 创建并定义解释文字的DOM元素
const explanationText = document.createElement("div");
explanationText.innerText =
"💬请设置翻页热键\n💡选中文本框后直接按下目标热键试试吧:";
explanationText.style =
"margin-bottom: 10px; display: block; width: 100%;";
hotkeyWindow.appendChild(explanationText);
const createHotkeySetting = (labelText, currentKey, onKeyChange) => {
const container = document.createElement("div");
container.style = `
display: flex;
justify-content: space-between;
width: 100%;
margin-bottom: 10px;
`;
const label = document.createElement("label");
label.innerText = labelText;
container.appendChild(label);
const input = document.createElement("input");
input.type = "text";
input.value = currentKey;
input.style = `
width: 100px;
background-color: ${isBlackMode ? "black" : "white"};
color: ${isBlackMode ? "white" : "black"};
`;
input.onkeydown = (event) => {
event.preventDefault();
input.value = event.key;
onKeyChange(event.key);
};
container.appendChild(input);
return container;
};
const upKeySetting = createHotkeySetting("上翻页热键", upKey, (key) => {
upKey = key;
});
const downKeySetting = createHotkeySetting(
"下翻页热键",
downKey,
(key) => {
downKey = key;
},
);
const hotkeySaveButton = document.createElement("button");
hotkeySaveButton.innerHTML = "💾保存";
hotkeySaveButton.style = `
margin-top: 10px;
background-color: ${isBlackMode ? "black" : "white"};
color: ${isBlackMode ? "white" : "black"};
`;
hotkeySaveButton.onclick = () => {
GM_setValue("upKey", upKey);
GM_setValue("downKey", downKey);
document.body.removeChild(hotkeyWindow);
};
hotkeyContainer.appendChild(upKeySetting);
hotkeyContainer.appendChild(downKeySetting);
hotkeyContainer.appendChild(hotkeySaveButton);
hotkeyWindow.appendChild(hotkeyContainer);
document.body.appendChild(hotkeyWindow);
});
settingsContainer.appendChild(hotkeysToggleSetting);
settingsContainer.appendChild(hotkeySetting);
// 创建按钮容器
const buttonContainer = document.createElement("div");
buttonContainer.style = `
display: flex;
justify-content: space-between;
width: 100%;
margin-top: 10px;
`;
// 修改保存按钮的样式并添加到按钮容器中
saveButton.style = `
flex: 1;
margin-right: 10px;
background-color: ${isBlackMode ? "black" : "white"};
color: ${isBlackMode ? "white" : "black"};
`;
buttonContainer.appendChild(saveButton);
// 修改检查更新/打赏按钮的样式并添加到按钮容器中
donateButton.style = `
flex: 1;
background-color: ${isBlackMode ? "black" : "white"};
color: ${isBlackMode ? "white" : "black"};
`;
buttonContainer.appendChild(donateButton);
// 将按钮容器添加到设置窗口中
settingsContainer.appendChild(buttonContainer);
settingsWindow.appendChild(settingsContainer);
document.body.appendChild(settingsWindow);
updateColors();
}
// 创建并显示悬浮窗
createFloatingWindow();
// 注册菜单命令
GM_registerMenuCommand("显示悬浮窗", function () {
GM_setValue("floatWindowVisible", true);
location.reload();
});
// 样式辅助函数
function buttonStyle() {
return `
width: ${floatWindowSize}px;
height: ${floatWindowSize}px;
font-size: 20px;
background-color: ${isBlackMode ? "black" : "white"};
color: ${isBlackMode ? "white" : "black"};
border: none;
opacity: ${textOpacity};
`;
}
})();