Greasy Fork is available in English.
在网页版微信读书右下角显示当前阅读进度
// ==UserScript==
// @name VBookProcess - Weread Progress Show
// @namespace https://github.com/chan1919/VBookProcess
// @version 1.0.0
// @description 在网页版微信读书右下角显示当前阅读进度
// @match https://weread.qq.com/*
// @grant GM_addStyle
// @license MIT
// @run-at document-idle
// @supportURL https://github.com/chan1919/VBookProcess/issues
// @homepageURL https://github.com/chan1919/VBookProcess
// ==/UserScript==
(function () {
'use strict';
let currentChapterRatio = 0;
let nextChapterRatio = 0;
let result = 0;
let chapterObserver = null;
let initAttempts = 0;
const MAX_ATTEMPTS = 30;
GM_addStyle(`
#weread-progress-box {
position: fixed !important;
bottom: 60px !important;
right: 5px !important;
width: 50px !important;
height: 30px !important;
font-size: 12px !important;
padding: 0 !important;
align-items: center !important;
text-align: center !important;
display: flex !important;
justify-content: center !important;
background-color: #6b6b6b !important;
color: #fff !important;
border-radius: 5px !important;
z-index: 2147483647 !important;
pointer-events: none !important;
}
`);
function getChapterItems() {
const items = document.querySelectorAll('.readerCatalog_list_item');
return items;
}
function isOnReaderPage() {
return window.location.href.includes('/web/reader/') ||
document.querySelector('.readerContent') !== null ||
document.querySelector('.readerCatalog_list') !== null;
}
function updateChapterRatio() {
const chapterItems = getChapterItems();
if (chapterItems.length === 0) {
return false;
}
const chapterTotal = chapterItems.length;
let found = false;
for (let i = 0; i < chapterTotal; i++) {
const className = chapterItems[i].className || '';
if (className.includes('readerCatalog_list_item_selected')) {
currentChapterRatio = parseFloat(((i / chapterTotal) * 100).toFixed(1));
nextChapterRatio = parseFloat((((i + 1) / chapterTotal) * 100).toFixed(1));
result = currentChapterRatio;
updateProgressBox();
found = true;
break;
}
}
return found;
}
function watchChapterChange() {
const chapterItems = getChapterItems();
if (chapterItems.length === 0) {
return false;
}
updateChapterRatio();
if (chapterObserver) {
chapterObserver.disconnect();
}
chapterObserver = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
if (mutation.type === 'attributes') {
updateChapterRatio();
}
});
});
chapterItems.forEach(item => {
chapterObserver.observe(item, { attributes: true });
});
return true;
}
function updateScrollRatio() {
const scrollPosition = window.scrollY || window.pageYOffset;
const docHeight = document.documentElement.scrollHeight;
const winHeight = window.innerHeight;
const maxScrollPosition = docHeight - winHeight;
if (maxScrollPosition > 0) {
const scrollRatio = scrollPosition / maxScrollPosition;
const totalRatio = (nextChapterRatio - currentChapterRatio) * scrollRatio + currentChapterRatio;
result = parseFloat(totalRatio.toFixed(1));
updateProgressBox();
}
}
function updateProgressBox() {
let progressBox = document.querySelector('#weread-progress-box');
if (!progressBox) {
progressBox = document.createElement('div');
progressBox.id = 'weread-progress-box';
document.body.appendChild(progressBox);
}
progressBox.textContent = result + '%';
}
function tryInit() {
if (!isOnReaderPage()) {
return;
}
initAttempts++;
if (initAttempts > MAX_ATTEMPTS) {
return;
}
updateProgressBox();
const success = watchChapterChange();
if (!success) {
setTimeout(tryInit, 500);
}
}
function init() {
updateProgressBox();
tryInit();
window.addEventListener('scroll', updateScrollRatio, { passive: true });
let lastUrl = location.href;
new MutationObserver(() => {
const url = location.href;
if (url !== lastUrl) {
lastUrl = url;
initAttempts = 0;
setTimeout(tryInit, 1000);
}
}).observe(document, { subtree: true, childList: true });
const bodyObserver = new MutationObserver(function (mutations) {
const chapterItems = getChapterItems();
if (chapterItems.length > 0 && !chapterObserver) {
watchChapterChange();
}
});
bodyObserver.observe(document.body, {
childList: true,
subtree: true
});
}
if (document.readyState === 'complete' || document.readyState === 'interactive') {
setTimeout(init, 500);
} else {
window.addEventListener('load', function() {
setTimeout(init, 500);
});
}
})();