Greasy Fork is available in English.

VBookProcess - Weread Progress Show

在网页版微信读书右下角显示当前阅读进度

Du musst eine Erweiterung wie Tampermonkey, Greasemonkey oder Violentmonkey installieren, um dieses Skript zu installieren.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

Sie müssten eine Skript Manager Erweiterung installieren damit sie dieses Skript installieren können

(Ich habe schon ein Skript Manager, Lass mich es installieren!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==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);
        });
    }

})();