Greasy Fork

Greasy Fork is available in English.

修复B站播放器性能BUG

修复 Bilibili 播放器中由于某些 rAF 回调函数引起的性能问题。

当前为 2025-07-14 提交的版本,查看 最新版本

// ==UserScript==
// @name         修复B站播放器性能BUG
// @version      2025.07.14.5
// @description  修复 Bilibili 播放器中由于某些 rAF 回调函数引起的性能问题。
// @author       嘉友友
// @match        https://www.bilibili.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=bilibili.com
// @grant        none
// @license      GNU GPLv3
// @namespace http://greasyfork.icu/users/1336389
// ==/UserScript==
(function() {
    'use strict';

    // 从本地存储读取状态,默认为开启
    let isFixEnabled = localStorage.getItem('bilibili-fix-enabled') !== 'false';
    let old_raf = window.requestAnimationFrame;

    // 保存状态到本地存储
    function saveState() {
        localStorage.setItem('bilibili-fix-enabled', isFixEnabled.toString());
    }

    // 应用修复的函数
    function applyFix() {
        if (isFixEnabled) {
            window.requestAnimationFrame = (fn) => {
                let src = ''+fn;
                if(src.length<1000 && src.includes('.fpsArr.')) {
                    console.log('!!! removed bad rAF');
                } else {
                    return old_raf(fn);
                }
            };
        } else {
            window.requestAnimationFrame = old_raf;
        }
    }

    // 创建可拖动的开关按钮
    function createToggleButton() {
        const button = document.createElement('div');
        button.id = 'bilibili-fix-toggle';

        // 从本地存储读取按钮位置
        const savedPosition = JSON.parse(localStorage.getItem('bilibili-fix-position') || '{}');
        const defaultTop = savedPosition.top || '20px';
        const defaultRight = savedPosition.right || '20px';
        const defaultLeft = savedPosition.left || 'auto';
        const defaultBottom = savedPosition.bottom || 'auto';

        button.style.cssText = `
            position: fixed;
            top: ${defaultTop};
            right: ${defaultRight};
            left: ${defaultLeft};
            bottom: ${defaultBottom};
            width: 40px;
            height: 40px;
            border-radius: 50%;
            background-color: ${isFixEnabled ? '#1890ff' : '#d9d9d9'};
            opacity: 0.6;
            cursor: move;
            z-index: 10000;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-size: 12px;
            font-weight: bold;
            user-select: none;
            transition: all 0.3s ease;
            box-shadow: 0 2px 8px rgba(0,0,0,0.15);
            border: 2px solid white;
        `;
        button.textContent = '修复';
        button.title = `点击${isFixEnabled ? '关闭' : '开启'}修复功能`;

        // 鼠标悬停效果
        button.addEventListener('mouseenter', () => {
            button.style.opacity = '1';
            button.style.transform = 'scale(1.1)';
        });

        button.addEventListener('mouseleave', () => {
            button.style.opacity = '0.6';
            button.style.transform = 'scale(1)';
        });

        // 拖动功能
        let isDragging = false;
        let dragStartX, dragStartY;
        let buttonStartX, buttonStartY;

        // 保存按钮位置
        function savePosition() {
            const rect = button.getBoundingClientRect();
            const position = {
                top: button.style.top,
                right: button.style.right,
                left: button.style.left,
                bottom: button.style.bottom
            };
            localStorage.setItem('bilibili-fix-position', JSON.stringify(position));
        }

        button.addEventListener('mousedown', (e) => {
            isDragging = true;
            dragStartX = e.clientX;
            dragStartY = e.clientY;
            buttonStartX = button.offsetLeft;
            buttonStartY = button.offsetTop;
            button.style.cursor = 'grabbing';
            e.preventDefault();
        });

        document.addEventListener('mousemove', (e) => {
            if (!isDragging) return;

            const deltaX = e.clientX - dragStartX;
            const deltaY = e.clientY - dragStartY;

            let newX = buttonStartX + deltaX;
            let newY = buttonStartY + deltaY;

            // 限制在窗口范围内
            newX = Math.max(0, Math.min(newX, window.innerWidth - button.offsetWidth));
            newY = Math.max(0, Math.min(newY, window.innerHeight - button.offsetHeight));

            button.style.left = newX + 'px';
            button.style.top = newY + 'px';
            button.style.right = 'auto';
            button.style.bottom = 'auto';
        });

        document.addEventListener('mouseup', (e) => {
            if (!isDragging) return;

            isDragging = false;
            button.style.cursor = 'move';

            // 判断是否为点击而非拖动
            const deltaX = Math.abs(e.clientX - dragStartX);
            const deltaY = Math.abs(e.clientY - dragStartY);

            if (deltaX < 5 && deltaY < 5) {
                // 切换开关状态
                isFixEnabled = !isFixEnabled;
                updateButtonAppearance();
                applyFix();
                saveState(); // 保存状态
                console.log(`!!! rAF fix ${isFixEnabled ? 'enabled' : 'disabled'}`);
            } else {
                // 如果是拖动,保存位置
                savePosition();
            }
        });

        function updateButtonAppearance() {
            button.style.backgroundColor = isFixEnabled ? '#1890ff' : '#d9d9d9';
            button.title = `点击${isFixEnabled ? '关闭' : '开启'}修复功能`;
        }

        document.body.appendChild(button);
        return button;
    }

    // 等待页面加载完成后创建按钮
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', createToggleButton);
    } else {
        createToggleButton();
    }

    // 初始应用修复
    applyFix();
    console.log(`!!! rAF hook set (${isFixEnabled ? 'enabled' : 'disabled'})`);
})();