VBookProcess - Weread Progress Show

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

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Для установки этого скрипта вам необходимо установить расширение, такое как Tampermonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

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

})();