多种颜色护眼模式、调整页面宽度(状态持久化,页面刷新不变)、自动/挂机阅读(增加定时模式,单双烂阅读通用)
当前为
// ==UserScript==
// @name 微信读书多功能插件
// @version 0.8.3
// @namespace http://tampermonkey.net/
// @description 多种颜色护眼模式、调整页面宽度(状态持久化,页面刷新不变)、自动/挂机阅读(增加定时模式,单双烂阅读通用)
// @author Chloe
// @match https://weread.qq.com/web/reader/*
// @icon https://weread.qq.com/favicon.ico
// @require https://code.jquery.com/jquery-3.7.1.min.js
// @grant GM_log
// @grant GM_addStyle
// @grant unsafeWindow
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_openInTab
// @grant GM_download
// @grant GM_setClipboard
// @grant GM_notification
// @license MIT
// ==/UserScript==
(function () {
'use strict';
// 常量定义
const DEFAULT_WIDTH = 800;
const EYE_PROTECTION_COLORS = {
'green': { name: '绿色', color: 'rgba(216,226,200,1)' },
'yellow': { name: '黄色', color: 'rgba(240,234,214,1)' },
'blue': { name: '蓝色', color: 'rgba(200,220,240,1)' },
'pink': { name: '粉色', color: 'rgba(255,230,230,1)' },
'purple': { name: '紫色', color: 'rgba(230,220,250,1)' },
'gray': { name: '灰色', color: 'rgba(240,240,240,1)' }
};
// 状态变量 - 从存储中恢复
let scrollInterval = null;
let timerInterval = null;
let isAutoReading = GM_getValue('weread_auto_reading', false);
let isPageTurning = false;
let pageTurnCooldown = false;
let currentScrollSpeed = GM_getValue('weread_scroll_speed', 1.0);
let remainingTime = GM_getValue('weread_remaining_time', 0);
let lastTimerValue = GM_getValue('weread_last_timer', 0);
let windowTop = 0;
// 自动翻页相关变量
let bottomReachedTimer = null;
let isWaitingForPageTurn = false;
let lastScrollPosition = 0;
let progressInterval = null;
// 样式注入
GM_addStyle(`
*{font-family: TsangerJinKai05 !important;}
.readerTopBar{font-family: SourceHanSerifCN-Bold !important;}
.bookInfo_title{font-family: SourceHanSerifCN-Bold !important;}
.readerTopBar_title_link{font-family: SourceHanSerifCN-Bold; !important; font-weight:bold !important;}
.readerTopBar_title_chapter{font-family: SourceHanSerifCN-Bold !important;}
.readerChapterContent{color: rgba(0,0,0,100) !important;}
.readerControls{margin-left: calc(50% - 60px) !important; margin-bottom: -28px !important;}
/* 控制面板样式 */
.control-panel {
position: fixed;
right: 40px;
top: 50%;
transform: translateY(-50%);
border: 1px solid #ddd;
border-radius: 8px;
padding: 30px 15px 15px 15px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
z-index: 99998;
min-width: 150px;
transition: background-color 0.3s ease;
}
.control-panel-close {
position: absolute;
right: 8px;
top: 8px;
background: none;
border: none;
font-size: 16px;
cursor: pointer;
color: #999;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
}
.control-panel-close:hover {
background: #f0f0f0;
color: #333;
}
.control-section {
margin: 15px 0;
padding-bottom: 15px;
border-bottom: 1px solid #eee;
}
.control-section:last-child {
border-bottom: none;
padding-bottom: 0;
}
.control-section-title {
font-size: 14px;
font-weight: bold;
color: #333;
margin-bottom: 10px;
text-align: center;
}
.control-item {
margin: 10px 0;
display: flex;
align-items: center;
justify-content: space-between;
}
.control-label {
font-size: 12px;
color: #666;
margin-right: 10px;
}
.control-slider {
width: 120px;
height: 4px;
background: #ddd;
outline: none;
opacity: 0.7;
transition: opacity .2s;
border-radius: 2px;
}
.control-slider:hover {
opacity: 1;
}
.timer-slider {
background: linear-gradient(to right,
#4CAF50 0%, #4CAF50 4.17%,
#ddd 4.17%, #ddd 12.5%,
#4CAF50 12.5%, #4CAF50 25%,
#ddd 25%, #ddd 50%,
#4CAF50 50%, #4CAF50 100%);
}
.speed-slider {
background: linear-gradient(to right,
#4CAF50 0%, #4CAF50 10%,
#ddd 10%, #ddd 20%,
#4CAF50 20%, #4CAF50 30%,
#ddd 30%, #ddd 40%,
#4CAF50 40%, #4CAF50 60%,
#ddd 60%, #ddd 100%);
}
.control-value {
font-size: 12px;
color: #333;
min-width: 40px;
text-align: center;
font-family: monospace;
}
.control-buttons {
display: flex;
gap: 5px;
margin-top: 10px;
flex-wrap: wrap;
}
.control-btn {
flex: 1;
padding: 6px 8px;
font-size: 12px;
border: 1px solid #ddd;
background: #f5f5f5;
border-radius: 4px;
cursor: pointer;
text-align: center;
min-width: 60px;
}
.control-btn:hover {
background: #e9e9e9;
}
.control-btn.active {
background: #4CAF50;
color: white;
border-color: #4CAF50;
}
.control-btn.reset {
background: #ff9800;
color: white;
border-color: #ff9800;
}
.control-btn.reset:hover {
background: #f57c00;
}
.control-btn.disabled {
background: #cccccc;
color: #666666;
cursor: not-allowed;
border-color: #cccccc;
}
.control-btn.secondary {
background: #e0e0e0;
color: #333;
border-color: #bdbdbd;
}
.color-options {
display: flex;
gap: 10px;
margin: 10px 0;
justify-content: center;
flex-wrap: wrap;
}
.color-option-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 5px;
cursor: pointer;
}
.color-option {
width: 30px;
height: 30px;
border-radius: 50%;
border: 2px solid #ddd;
cursor: pointer;
transition: all 0.2s;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.color-option:hover {
transform: scale(1.1);
box-shadow: 0 3px 6px rgba(0,0,0,0.15);
}
.color-option.active {
border-color: #333;
transform: scale(1.1);
box-shadow: 0 3px 8px rgba(0,0,0,0.2);
}
.color-name {
font-size: 10px;
color: #666;
text-align: center;
min-width: 40px;
}
.color-green { background-color: rgba(216,226,200,1); }
.color-yellow { background-color: rgba(240,234,214,1); }
.color-blue { background-color: rgba(200,220,240,1); }
.color-pink { background-color: rgba(255,230,230,1); }
.color-purple { background-color: rgba(230,220,250,1); }
.color-gray { background-color: rgba(240,240,240,1); }
/* 提示框样式 */
.custom-notification {
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 10px 20px;
border-radius: 4px;
z-index: 100000;
font-size: 14px;
transition: opacity 0.3s ease-in-out;
}
.custom-notification.fade-out {
opacity: 0;
}
/* 护眼模式样式 */
.eye-protection-green body {
background-color: rgba(216,226,200,1) !important;
}
.eye-protection-green .readerContent .app_content {
background-color: rgba(216,226,200,1) !important;
}
.eye-protection-green .readerTopBar {
background-color: rgba(216,226,200,1) !important;
}
.eye-protection-yellow body {
background-color: rgba(240,234,214,1) !important;
}
.eye-protection-yellow .readerContent .app_content {
background-color: rgba(240,234,214,1) !important;
}
.eye-protection-yellow .readerTopBar {
background-color: rgba(240,234,214,1) !important;
}
.eye-protection-blue body {
background-color: rgba(200,220,240,1) !important;
}
.eye-protection-blue .readerContent .app_content {
background-color: rgba(200,220,240,1) !important;
}
.eye-protection-blue .readerTopBar {
background-color: rgba(200,220,240,1) !important;
}
.eye-protection-pink body {
background-color: rgba(255,230,230,1) !important;
}
.eye-protection-pink .readerContent .app_content {
background-color: rgba(255,230,230,1) !important;
}
.eye-protection-pink .readerTopBar {
background-color: rgba(255,230,230,1) !important;
}
.eye-protection-purple body {
background-color: rgba(230,220,250,1) !important;
}
.eye-protection-purple .readerContent .app_content {
background-color: rgba(230,220,250,1) !important;
}
.eye-protection-purple .readerTopBar {
background-color: rgba(230,220,250,1) !important;
}
.eye-protection-gray body {
background-color: rgba(240,240,240,1) !important;
}
.eye-protection-gray .readerContent .app_content {
background-color: rgba(240,240,240,1) !important;
}
.eye-protection-gray .readerTopBar {
background-color: rgba(240,240,240,1) !important;
}
/* 定时器显示样式 */
.timer-display {
font-size: 12px;
color: #666;
text-align: center;
margin-top: 5px;
}
/* 分割条样式 */
.section-divider {
height: 1px;
background: linear-gradient(to right, transparent, #ddd, transparent);
margin: 10px 0;
}
/* 复制成功提示框 */
#module_box {
position: fixed;
left: 0;
top: 200px;
bottom: 0;
right: 0;
margin: auto;
width: 200px;
height: 100px;
text-align: center;
line-height: 100px;
background-color: rgba(0, 0, 0, 0.3);
font-size: 24px;
z-index: 999999;
display: none;
}
/* 自动翻页进度条样式 */
#auto-turn-progress {
position: fixed;
bottom: 20px;
right: 20px;
background: white;
border: 1px solid #ddd;
border-radius: 8px;
padding: 10px 15px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
z-index: 99997;
min-width: 160px;
display: none;
}
.progress-text {
font-size: 12px;
color: #333;
margin-bottom: 5px;
text-align: center;
}
.progress-bar {
width: 100%;
height: 6px;
background: #f0f0f0;
border-radius: 3px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: #2196F3;
border-radius: 3px;
transition: width 0.1s linear;
width: 100%;
}
/* 设置按钮图标样式 */
.settings-icon {
display: inline-block;
width: 16px;
height: 16px;
background: currentColor;
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.22,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.22,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.68 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z'/%3E%3C/svg%3E") no-repeat center;
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.22,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.22,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.68 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z'/%3E%3C/svg%3E") no-repeat center;
}
`);
// 工具函数
const utils = {
// 显示通知
notificationManager: {
currentNotification: null,
timeoutId: null,
show: function (message, duration = 3000) {
this.clear();
this.currentNotification = $(`<div class="custom-notification">${message}</div>`);
$('body').append(this.currentNotification);
this.timeoutId = setTimeout(() => {
this.close();
}, duration);
},
close: function () {
if (this.currentNotification) {
this.currentNotification.addClass('fade-out');
setTimeout(() => {
if (this.currentNotification && this.currentNotification.parent().length) {
this.currentNotification.remove();
}
this.currentNotification = null;
}, 300);
}
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = null;
}
},
clear: function () {
this.close();
$('.custom-notification').remove();
}
},
// 检测DOM元素出现
waitForElement: function (selector, maxAttempts = 80) {
return new Promise(resolve => {
let attempts = 0;
const checkInterval = setInterval(() => {
if (document.querySelectorAll(selector).length) {
clearInterval(checkInterval);
resolve(true);
}
if (attempts >= maxAttempts) {
clearInterval(checkInterval);
resolve(false);
}
attempts++;
}, 100);
});
},
// 检查当前是否是白色主题
isWhiteTheme: function () {
return document.body.classList.contains('wr_whiteTheme');
},
// 获取页面背景颜色并转换为不透明
getPageBackgroundColor: function () {
const appContent = document.querySelector('.readerContent .app_content');
if (appContent) {
let bgColor = window.getComputedStyle(appContent).backgroundColor;
// 将颜色转换为不透明
if (bgColor.startsWith('rgba')) {
const rgbaMatch = bgColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
if (rgbaMatch) {
const r = rgbaMatch[1];
const g = rgbaMatch[2];
const b = rgbaMatch[3];
return `rgb(${r}, ${g}, ${b})`;
}
}
return bgColor;
}
return 'rgb(255, 255, 255)';
}
};
// 宽度控制功能 - 确保状态正确保存和恢复
const widthControl = {
init: function () {
const savedWidth = GM_getValue('weread_max_width', DEFAULT_WIDTH);
this.applyWidth(savedWidth);
return savedWidth;
},
applyWidth: function (width) {
const content = document.querySelector(".readerContent .app_content");
const topBar = document.querySelector('.readerTopBar');
if (content && topBar) {
content.style.maxWidth = width + 'px';
topBar.style.maxWidth = width + 'px';
// 立即保存设置
GM_setValue('weread_max_width', width);
// 更新滑块显示
if ($('#widthSlider').length) {
$('#widthSlider').val(width);
$('#widthValue').text(width + 'px');
}
const resizeEvent = new Event('resize');
window.dispatchEvent(resizeEvent);
}
},
reset: function () {
this.applyWidth(DEFAULT_WIDTH);
utils.notificationManager.show('已恢复默认宽度');
},
// 获取当前实际宽度
getCurrentWidth: function () {
const content = document.querySelector(".readerContent .app_content");
if (content) {
const style = window.getComputedStyle(content);
return parseInt(style.maxWidth) || DEFAULT_WIDTH;
}
return DEFAULT_WIDTH;
}
};
// 护眼模式功能 - 改进的主题检测逻辑
const eyeProtection = {
init: function () {
const isEnabled = GM_getValue('weread_eye_protection', false);
const selectedColor = GM_getValue('weread_eye_protection_color', 'green');
const isWhite = utils.isWhiteTheme();
// 更新颜色选择按钮
$(`.color-option`).removeClass('active');
$(`.color-option.color-${selectedColor}`).addClass('active');
// 关键改进:如果不是白色主题,直接禁用按钮并关闭护眼模式
if (!isWhite) {
// 强制关闭护眼模式
if (isEnabled) {
GM_setValue('weread_eye_protection', false);
this.disable();
}
// 禁用按钮
$('#eyeProtectionBtn').addClass('disabled');
$('#eyeProtectionBtn').text('护眼模式:关');
// 显示提示
if (isEnabled) {
utils.notificationManager.show('护眼模式仅在白色主题下可用,已自动关闭');
}
return false;
}
// 如果是白色主题,正常初始化
if (isEnabled) {
this.enable(selectedColor);
$('#eyeProtectionBtn').removeClass('disabled');
} else {
this.disable();
$('#eyeProtectionBtn').removeClass('disabled');
}
return isEnabled;
},
enable: function (color) {
Object.keys(EYE_PROTECTION_COLORS).forEach(colorKey => {
document.body.classList.remove(`eye-protection-${colorKey}`);
});
document.body.classList.add(`eye-protection-${color}`);
$('#eyeProtectionBtn').addClass('active');
$('#eyeProtectionBtn').text('护眼模式:开');
},
disable: function () {
Object.keys(EYE_PROTECTION_COLORS).forEach(color => {
document.body.classList.remove(`eye-protection-${color}`);
});
$('#eyeProtectionBtn').removeClass('active');
$('#eyeProtectionBtn').text('护眼模式:关');
},
toggle: function () {
// 检查是否禁用 - 如果不是白色主题,按钮应该已经被禁用
if ($('#eyeProtectionBtn').hasClass('disabled')) {
utils.notificationManager.show('护眼模式仅在白色主题下可用');
return;
}
const isActive = $('#eyeProtectionBtn').hasClass('active');
const selectedColor = GM_getValue('weread_eye_protection_color', 'green');
const colorInfo = EYE_PROTECTION_COLORS[selectedColor];
if (isActive) {
this.disable();
GM_setValue('weread_eye_protection', false);
utils.notificationManager.show('护眼模式已关闭');
} else {
this.enable(selectedColor);
GM_setValue('weread_eye_protection', true);
utils.notificationManager.show(`${colorInfo.name}护眼模式已开启`);
}
},
changeColor: function (color) {
// 检查是否禁用
if ($('#eyeProtectionBtn').hasClass('disabled')) {
utils.notificationManager.show('护眼模式仅在白色主题下可用');
return;
}
const colorInfo = EYE_PROTECTION_COLORS[color];
GM_setValue('weread_eye_protection_color', color);
// 如果护眼模式已开启,立即应用新颜色
if ($('#eyeProtectionBtn').hasClass('active')) {
this.enable(color);
utils.notificationManager.show(`已切换为${colorInfo.name}护眼模式`);
}
}
};
// 自动翻页功能
const autoPageTurn = {
trigger: function () {
if (isPageTurning || pageTurnCooldown) return;
isPageTurning = true;
// 触发键盘右键事件
const event = new KeyboardEvent('keydown', {
bubbles: true,
cancelable: true,
key: 'ArrowRight',
code: 'ArrowRight',
keyCode: 39,
charCode: 0
});
document.dispatchEvent(event);
// 也触发keyup事件
const keyUpEvent = new KeyboardEvent('keyup', {
bubbles: true,
cancelable: true,
key: 'ArrowRight',
code: 'ArrowRight',
keyCode: 39,
charCode: 0
});
document.dispatchEvent(keyUpEvent);
console.log('触发自动翻页');
// 设置冷却时间,防止连续触发
pageTurnCooldown = true;
setTimeout(() => {
pageTurnCooldown = false;
}, 2000);
// 翻页后等待页面加载完成
setTimeout(() => {
isPageTurning = false;
// 翻页后自动滚动到页面顶部附近,确保内容可见
window.scrollTo(0, 100);
}, 1500);
}
};
// 自动翻页进度条
const progressBar = {
init: function() {
$('body').append(`
<div id="auto-turn-progress">
<div class="progress-text">0秒后自动翻页</div>
<div class="progress-bar">
<div class="progress-fill"></div>
</div>
</div>
`);
},
show: function(waitTime) {
this.waitTime = waitTime;
this.startTime = Date.now();
$('#auto-turn-progress').show();
this.update();
progressInterval = setInterval(() => {
this.update();
}, 100);
},
update: function() {
const elapsed = (Date.now() - this.startTime) / 1000;
const remaining = Math.max(0, this.waitTime - elapsed);
const percentage = (remaining / this.waitTime) * 100;
$('.progress-text').text(`${remaining.toFixed(1)}秒后自动翻页`);
$('.progress-fill').css('width', percentage + '%');
if (remaining <= 0) {
this.hide();
}
},
hide: function() {
$('#auto-turn-progress').hide();
if (progressInterval) {
clearInterval(progressInterval);
progressInterval = null;
}
}
};
// 自动阅读功能 - 添加状态保存和恢复
const autoRead = {
// 根据速度计算等待时间
calculateWaitTime: function() {
const speed = currentScrollSpeed;
if (speed <= 0.5) return 10;
if (speed <= 1) return 8;
if (speed <= 2) return 6;
if (speed <= 3) return 4;
return 2; // 4倍及以上速度
},
start: function () {
// 先停止可能存在的滚动
if (scrollInterval) {
clearInterval(scrollInterval);
scrollInterval = null;
}
// 清除可能存在的底部计时器
this.clearBottomTimer();
// 记录定时时间
const timerMinutes = parseInt($('#timerSlider').val());
if (timerMinutes > 0) {
lastTimerValue = timerMinutes;
GM_setValue('weread_last_timer', lastTimerValue);
this.updateLastTimerButton();
}
const baseSpeed = 1;
const speedMultiplier = currentScrollSpeed;
let lastScrollTop = document.documentElement.scrollTop || document.body.scrollTop;
let stuckCount = 0;
scrollInterval = setInterval(() => {
// 如果正在翻页,暂停滚动但保持计时
if (isPageTurning) {
return;
}
const currentScrollTop = document.documentElement.scrollTop || document.body.scrollTop;
const scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
const clientHeight = document.documentElement.clientHeight || document.body.clientHeight;
// 检查是否到达底部
if (currentScrollTop + clientHeight >= scrollHeight - 10) {
// 所有模式统一处理:等待指定时间后自动翻页
if (!isWaitingForPageTurn) {
this.schedulePageTurn();
}
return;
}
// 检查是否卡住
if (currentScrollTop === lastScrollTop) {
stuckCount++;
if (stuckCount > 5) {
window.scrollBy(0, baseSpeed * speedMultiplier * 3);
} else {
window.scrollBy(0, baseSpeed * speedMultiplier);
}
} else {
stuckCount = 0;
window.scrollBy(0, baseSpeed * speedMultiplier);
}
lastScrollTop = currentScrollTop;
// 记录滚动位置用于检测用户手动翻页
lastScrollPosition = currentScrollTop;
}, 20);
isAutoReading = true;
this.updateButton();
// 启动定时器
this.startTimer();
// 保存状态
this.saveState();
utils.notificationManager.show('自动阅读已开始');
},
stop: function () {
if (scrollInterval) {
clearInterval(scrollInterval);
scrollInterval = null;
}
isAutoReading = false;
isPageTurning = false;
isWaitingForPageTurn = false;
this.updateButton();
// 清除底部计时器
this.clearBottomTimer();
// 隐藏进度条
progressBar.hide();
// 停止定时器
this.stopTimer();
// 保存状态
this.saveState();
},
toggle: function () {
if (isAutoReading) {
this.stop();
} else {
this.start();
}
},
// 安排自动翻页
schedulePageTurn: function () {
isWaitingForPageTurn = true;
const waitTime = this.calculateWaitTime();
console.log(`到达底部,等待${waitTime}秒后自动翻页`);
// 显示进度条
progressBar.show(waitTime);
bottomReachedTimer = setTimeout(() => {
if (isWaitingForPageTurn) {
console.log('等待结束,执行自动翻页');
autoPageTurn.trigger();
isWaitingForPageTurn = false;
progressBar.hide();
// 翻页后重新开始滚动
setTimeout(() => {
if (isAutoReading) {
lastScrollPosition = 0;
}
}, 2000);
}
}, waitTime * 1000);
},
// 清除底部计时器
clearBottomTimer: function () {
if (bottomReachedTimer) {
clearTimeout(bottomReachedTimer);
bottomReachedTimer = null;
}
isWaitingForPageTurn = false;
progressBar.hide();
},
// 检查用户是否手动翻页
checkManualPageTurn: function () {
if (!isWaitingForPageTurn) return;
const currentScrollTop = document.documentElement.scrollTop || document.body.scrollTop;
// 如果滚动位置显著变化,认为用户手动翻页,重新计时
if (Math.abs(currentScrollTop - lastScrollPosition) > 50) {
console.log('检测到用户手动翻页,重新计时');
this.clearBottomTimer();
this.schedulePageTurn();
}
lastScrollPosition = currentScrollTop;
},
startTimer: function () {
const timerMinutes = parseInt($('#timerSlider').val());
if (timerMinutes > 0) {
// 如果是从恢复状态而来,可能已经设置了remainingTime,否则使用定时器滑块的值
if (remainingTime <= 0) {
remainingTime = timerMinutes * 60;
}
this.updateTimerDisplay();
timerInterval = setInterval(() => {
remainingTime--;
this.updateTimerDisplay();
// 每秒保存一次剩余时间
GM_setValue('weread_remaining_time', remainingTime);
if (remainingTime <= 0) {
this.stop();
utils.notificationManager.show('定时时间到,自动阅读已停止');
}
}, 1000);
}
},
stopTimer: function () {
if (timerInterval) {
clearInterval(timerInterval);
timerInterval = null;
}
remainingTime = 0;
GM_setValue('weread_remaining_time', 0);
this.updateTimerDisplay();
},
updateTimerDisplay: function () {
if (remainingTime > 0) {
const minutes = Math.floor(remainingTime / 60);
const seconds = remainingTime % 60;
$('#timerDisplay').text(`剩余: ${minutes}:${seconds.toString().padStart(2, '0')}`);
} else {
$('#timerDisplay').text('');
}
},
updateButton: function () {
const button = $('#toggleAutoRead');
if (isAutoReading) {
button.text('停止阅读');
button.addClass('active');
} else {
button.text('开始阅读');
button.removeClass('active');
}
},
updateLastTimerButton: function () {
const lastTimerBtn = $('#lastTimerBtn');
if (lastTimerValue > 0) {
lastTimerBtn.removeClass('disabled');
lastTimerBtn.css('background', '#e0e0e0');
} else {
lastTimerBtn.addClass('disabled');
lastTimerBtn.css('background', '#cccccc');
}
},
applyLastTimer: function () {
if (lastTimerValue > 0) {
$('#timerSlider').val(lastTimerValue);
$('#timerValue').text(lastTimerValue + '分钟');
utils.notificationManager.show(`已设置为上次定时时间: ${lastTimerValue}分钟`);
} else {
utils.notificationManager.show('没有找到上次定时时间');
}
},
// 保存自动阅读状态
saveState: function () {
GM_setValue('weread_auto_reading', isAutoReading);
GM_setValue('weread_scroll_speed', currentScrollSpeed);
},
// 恢复自动阅读状态
restoreState: function () {
if (isAutoReading) {
// 设置速度滑块
$('#speedSlider').val(currentScrollSpeed);
$('#speedValue').text(currentScrollSpeed.toFixed(1) + 'x');
// 设置定时器滑块
const timerMinutes = Math.ceil(remainingTime / 60);
if (timerMinutes > 0) {
$('#timerSlider').val(timerMinutes);
$('#timerValue').text(timerMinutes + '分钟');
}
// 更新按钮状态
this.updateButton();
// 开始自动阅读
this.start();
utils.notificationManager.show('已恢复自动阅读状态');
}
}
};
// 复制和图片下载功能
const contentTools = {
init: async function () {
// 添加复制成功提示框
$("body").append(`
<div id="module_box">
复制成功
</div>
`);
// 添加复制和图片下载按钮
await this.addCopyImgButtons();
},
addCopyImgButtons: async function () {
let resDomImg = await utils.waitForElement('.wr_readerImage_opacity');
let openImgBtnLength = $("button[name='btn_cxy_open_img_page']").length;
let getImgBtnLength = $("button[name='btn_cxy_get_img']").length;
if (resDomImg && openImgBtnLength === 0 && getImgBtnLength === 0) {
console.log("图片个数===", $('.wr_readerImage_opacity').length);
$('.wr_readerImage_opacity').each((ind, ele) => {
let btn = document.createElement("button");
btn.name = "btn_cxy_open_img_page";
btn.innerHTML = "📋";
btn.style.cssText = `position: absolute;right: 0px;top: ${ele.offsetTop - 50}px;color:white;z-index:9999; cursor:pointer`;
let btn2 = document.createElement("button");
btn2.name = "btn_cxy_get_img";
btn2.innerHTML = "▼";
btn2.style.cssText = `position: absolute;right: 0px;top: ${ele.offsetTop - 20}px;color:#888;z-index:9999; cursor:pointer`;
// 添加按钮
ele.after(btn);
ele.after(btn2);
});
}
},
copyCode: function () {
let codeText = $(this).closest('pre')[0].textContent.replace("📋", "");
GM_setClipboard(codeText);
$("#module_box").fadeIn();
setTimeout(() => {
$("#module_box").fadeOut(300, function () {
$(this).hide();
});
}, 1000);
},
openImagePage: function () {
let link = $(this).prev().prev().attr("src");
GM_openInTab(link, { active: true });
},
downloadImage: function () {
let link = $(this).prev().attr("src");
GM_download({
url: link,
name: new Date().getTime() + '.jpg',
headers: {
"User-Agent": "netdisk;6.7.1.9;PC;PC-Windows;10.0.17763;WindowsBaiduYunGuanJia",
},
onprogress: function (e) {
// 下载进度处理
},
});
}
};
// 控制面板功能 - 改进宽度初始化
const controlPanel = {
init: function () {
// 从存储中获取保存的宽度
const savedWidth = GM_getValue('weread_max_width', DEFAULT_WIDTH);
// 添加控制面板
$("body").append(`
<div class="control-panel" style="display: none;" id="mainControlPanel">
<button class="control-panel-close" id="closeControlPanel">×</button>
<div class="control-section">
<div class="control-section-title">宽度控制</div>
<div class="control-item">
<span class="control-label">页面宽度</span>
<input type="range" class="control-slider" id="widthSlider" min="600" max="1400" value="${savedWidth}">
<span class="control-value" id="widthValue">${savedWidth}px</span>
</div>
<div class="control-buttons">
<button class="control-btn reset" id="resetWidth">恢复默认</button>
</div>
</div>
<div class="section-divider"></div>
<div class="control-section">
<div class="control-section-title">自动阅读</div>
<div class="control-item">
<span class="control-label">阅读速度</span>
<input type="range" class="control-slider speed-slider" id="speedSlider" min="0.5" max="4" step="0.1" value="${currentScrollSpeed}">
<span class="control-value" id="speedValue">${currentScrollSpeed.toFixed(1)}x</span>
</div>
<div class="control-item">
<span class="control-label">定时关闭</span>
<input type="range" class="control-slider timer-slider" id="timerSlider" min="0" max="120" step="1" value="0">
<span class="control-value" id="timerValue">0分钟</span>
</div>
<div class="timer-display" id="timerDisplay"></div>
<div class="control-buttons">
<button class="control-btn" id="toggleAutoRead">${isAutoReading ? '停止阅读' : '开始阅读'}</button>
<button class="control-btn secondary" id="lastTimerBtn">上次定时</button>
</div>
</div>
<div class="section-divider"></div>
<div class="control-section">
<div class="control-section-title">显示设置</div>
<div class="color-options" id="colorOptionsContainer"></div>
<div class="control-buttons">
<button class="control-btn" id="eyeProtectionBtn">护眼模式:关</button>
</div>
</div>
</div>
`);
// 生成颜色选项
this.generateColorOptions();
// 添加控制按钮 - 使用图标按钮
$('.readerControls').append(`
<div class="wr_tooltip_container" style="--offset: 6px;">
<button class="readerControls_item" id="mainControl" style="color:#6a6c6c;cursor:pointer;">
<span class="settings-icon"></span>
</button>
<div class="wr_tooltip_item wr_tooltip_item--right" style="display: none;">
设置
</div>
</div>
`);
// 绑定事件
this.bindEvents();
},
generateColorOptions: function () {
const container = $('#colorOptionsContainer');
container.empty();
Object.keys(EYE_PROTECTION_COLORS).forEach(colorKey => {
const colorInfo = EYE_PROTECTION_COLORS[colorKey];
const isActive = colorKey === GM_getValue('weread_eye_protection_color', 'green');
const colorOption = $(`
<div class="color-option-container" data-color="${colorKey}">
<div class="color-option color-${colorKey} ${isActive ? 'active' : ''}"></div>
<div class="color-name">${colorInfo.name}</div>
</div>
`);
container.append(colorOption);
});
},
bindEvents: function () {
// 控制面板显示/隐藏
$('#mainControl').click(function () {
$('#mainControlPanel').toggle();
});
// 工具提示
$('#mainControl').hover(
function() {
$(this).siblings('.wr_tooltip_item').show();
},
function() {
$(this).siblings('.wr_tooltip_item').hide();
}
);
// 关闭按钮
$(document).on('click', '#closeControlPanel', function (e) {
e.stopPropagation();
$('#mainControlPanel').hide();
});
// 宽度控制 - 确保滑块值正确反映当前宽度
$('#widthSlider').on('input', function () {
const newWidth = parseInt($(this).val());
$('#widthValue').text(newWidth + 'px');
widthControl.applyWidth(newWidth);
});
$('#resetWidth').click(function () {
$('#widthSlider').val(DEFAULT_WIDTH);
$('#widthValue').text(DEFAULT_WIDTH + 'px');
widthControl.reset();
});
// 颜色选择
$(document).on('click', '.color-option-container', function () {
const color = $(this).data('color');
$('.color-option').removeClass('active');
$(this).find('.color-option').addClass('active');
eyeProtection.changeColor(color);
});
// 护眼模式切换
$(document).on('click', '#eyeProtectionBtn', function () {
if ($(this).hasClass('disabled')) {
utils.notificationManager.show('护眼模式仅在白色主题下可用');
return;
}
eyeProtection.toggle();
});
// 自动阅读控制
$('#speedSlider').on('input', function () {
currentScrollSpeed = parseFloat($(this).val());
$('#speedValue').text(currentScrollSpeed.toFixed(1) + 'x');
GM_setValue('weread_scroll_speed', currentScrollSpeed);
// 如果正在自动阅读,更新速度
if (isAutoReading) {
autoRead.stop();
autoRead.start();
}
});
$('#timerSlider').on('input', function () {
const timerValue = parseInt($(this).val());
$('#timerValue').text(timerValue + '分钟');
});
$('#lastTimerBtn').click(function () {
if ($(this).hasClass('disabled')) {
utils.notificationManager.show('没有可用的上次定时记录');
return;
}
autoRead.applyLastTimer();
});
$('#toggleAutoRead').click(function () {
autoRead.toggle();
});
// 点击页面其他地方隐藏控制面板
$(document).on('click', function (e) {
if (!$(e.target).closest('.control-panel').length &&
!$(e.target).closest('#mainControl').length &&
!$(e.target).closest('#closeControlPanel').length) {
$('.control-panel').hide();
}
});
},
updateBackground: function () {
const bgColor = utils.getPageBackgroundColor();
$('#mainControlPanel').css('background', bgColor);
}
};
// 头部隐藏功能
const headerControl = {
init: function () {
$(window).scroll(function () {
let scrollS = $(this).scrollTop();
let windowHeight = $(this).height();
let documentHeight = $(document).height();
// 头部隐藏逻辑
let selBtn = document.querySelector('.readerTopBar');
let readerControl = document.querySelector(".readerControls");
$('.readerControls').mouseenter(function () {
$('.readerControls').css('opacity', '1');
});
$('.readerControls').mouseleave(function () {
$('.readerControls').css('opacity', '0');
});
if (scrollS >= windowTop) {
selBtn.style.opacity = 0;
windowTop = scrollS;
} else {
selBtn.style.opacity = 1;
windowTop = scrollS;
}
// 检测用户手动翻页
if (isAutoReading) {
autoRead.checkManualPageTurn();
}
});
}
};
// 初始化函数
function initialize () {
// 初始化各模块
const currentWidth = widthControl.init();
// 关键改进:确保滑块值与实际宽度一致
$('#widthSlider').val(currentWidth);
$('#widthValue').text(currentWidth + 'px');
// 初始化护眼模式
eyeProtection.init();
// 初始化进度条
progressBar.init();
autoRead.updateLastTimerButton();
controlPanel.init();
headerControl.init();
contentTools.init();
// 初始化控制面板背景颜色
controlPanel.updateBackground();
// 恢复自动阅读状态(如果有)
if (isAutoReading) {
setTimeout(() => {
autoRead.restoreState();
}, 1000);
}
// 绑定复制和图片事件
$(document).on("click", "#copy_code", contentTools.copyCode);
$(document).on("click", "button[name='btn_cxy_open_img_page']", contentTools.openImagePage);
$(document).on("click", "button[name='btn_cxy_get_img']", contentTools.downloadImage);
// 监听主题变化和背景颜色变化
const observer = new MutationObserver(function (mutations) {
let shouldUpdate = false;
mutations.forEach(function (mutation) {
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
// 关键改进:主题变化时重新初始化护眼模式
setTimeout(() => {
eyeProtection.init();
}, 100);
shouldUpdate = true;
}
if (mutation.type === 'attributes' &&
(mutation.target.classList.contains('app_content') ||
mutation.target.classList.contains('readerContent') ||
document.body.classList.contains('eye-protection-'))) {
shouldUpdate = true;
}
});
if (shouldUpdate) {
setTimeout(() => {
controlPanel.updateBackground();
}, 100);
}
});
// 开始观察body和.app_content的变化
observer.observe(document.body, {
attributes: true,
attributeFilter: ['class']
});
const appContent = document.querySelector('.readerContent .app_content');
if (appContent) {
observer.observe(appContent, {
attributes: true,
attributeFilter: ['class', 'style']
});
}
// 添加窗口resize监听
$(window).on('resize', function () {
setTimeout(() => {
controlPanel.updateBackground();
}, 100);
});
}
// 页面加载完成后初始化
$(window).on('load', initialize);
})();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址