H5视频调速器 手机优化版

针对手机浏览器的H5视频调速器,多视频场景按视口距离优先控制

目前為 2025-08-04 提交的版本,檢視 最新版本

// ==UserScript==
// @name         H5视频调速器 手机优化版
// @namespace    http://tampermonkey.net/
// @version      0.2.3
// @description  针对手机浏览器的H5视频调速器,多视频场景按视口距离优先控制
// @author       Mr.NullNull
// @match        *://*/*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    var IntPbr = 1;
    var isPlaying = false;

    const IntPbrMax = 16;
    const IntPbrMin = 0.1;

    function Main() {
        if (document.querySelector("myPbrMain")) return;

        var myCss = document.createElement("style");
        myCss.innerHTML = `
            div#myPbrMain {
                padding: 0;
                margin: 0;
                width: auto;
                position: fixed;
                bottom: 28vw;
                right: 5vw;
                z-index: 2147483647;
                display: flex;
                flex-direction: column;
                align-items: flex-end;
            }
            div#myPbrMain>div.myPbrBtns {
                width: auto;
                margin-bottom: 0.8vw;
                display: none;
            }
            div.myPbrBtns {
                opacity: 0;
            }
            div.myPbrBtn {
                font: 4vw/1 '微软雅黑';
                height: 4vw;
                padding: 2vw;
                margin-bottom: 0.8vw;
                border-radius: 20vw;
                color: #eee;
                background-color: rgba(0, 0, 0, 0.65);
                cursor: pointer;
                white-space: nowrap;
                text-align: center;
                min-width: 12vw;
            }
            div#myPbrMain * {
                box-sizing: content-box;
                word-break: normal;
            }
            div.show {
                animation: shower 0.3s;
                opacity: 1;
                display: block !important;
            }
            div.hidden {
                animation: hiddener 0.3s;
                opacity: 0;
                display: none;
            }
            @keyframes shower {
                from { opacity: 0; }
                to { opacity: 1; }
            }
            @keyframes hiddener {
                from { opacity: 1; }
                to { opacity: 0; }
            }
        `;
        document.head.appendChild(myCss);

        var mainDivTop = document.createElement("div");
        mainDivTop.id = "myPbrMain";
        mainDivTop.innerHTML = `
            <div class="myPbrBtns hidden">
                <div class="myPbrBtn" id="myPbrBtn_500">x5.00</div>
                <div class="myPbrBtn" id="myPbrBtn_300">x3.00</div>
                <div class="myPbrBtn" id="myPbrBtn_200">x2.00</div>
                <div class="myPbrBtn" id="myPbrBtn_150">x1.50</div>
                <div class="myPbrBtn" id="myPbrBtn_100">x1.00</div>
            </div>
            <div class="myPbrBtn" id="myPbrBtn_Main">x1.XX</div>
            <div class="myPbrBtn" id="myPbrBtn_PlayPause">播放</div>
        `;
        document.body.appendChild(mainDivTop);

        var mainBtn = mainDivTop.querySelector("#myPbrBtn_Main");
        var speedPanel = mainDivTop.querySelector(".myPbrBtns");
        var playPauseBtn = mainDivTop.querySelector("#myPbrBtn_PlayPause");

        setMainBtnTxt();
        syncVideoStateToBtn();

        var syncTimer = setInterval(() => {
            syncVideoStateToBtn();
            const targetVideo = getTargetVideo();
            if (targetVideo) clearInterval(syncTimer);
        }, 500);
        setTimeout(() => clearInterval(syncTimer), 5000);

        mainBtn.onclick = function () {
            if (speedPanel.classList.contains("hidden")) {
                speedPanel.classList.remove("hidden");
                speedPanel.classList.add("show");
            } else {
                speedPanel.classList.remove("show");
                speedPanel.classList.add("hidden");
            }
        };

        playPauseBtn.onclick = function () {
            const targetVideo = getTargetVideo();
            if (targetVideo) {
                togglePlayPause(targetVideo);
            }
        };

        speedPanel.querySelector("#myPbrBtn_500").onclick = function () {
            setVideoSpeed(5);
        };
        speedPanel.querySelector("#myPbrBtn_300").onclick = function () {
            setVideoSpeed(3);
        };
        speedPanel.querySelector("#myPbrBtn_200").onclick = function () {
            setVideoSpeed(2.00);
        };
        speedPanel.querySelector("#myPbrBtn_150").onclick = function () {
            setVideoSpeed(1.50);
        };
        speedPanel.querySelector("#myPbrBtn_100").onclick = function () {
            setVideoSpeed(1.00);
        };

        // 核心优化:多视频时按“视口与视频的距离”优先判断目标视频
        function getTargetVideo() {
            const videos = document.querySelectorAll("video");
            if (videos.length === 0) return null;
            // 单视频直接返回
            if (videos.length === 1) return videos[0];

            // 获取用户当前视口的中心位置(Y轴坐标)
            const viewportCenterY = window.scrollY + (window.innerHeight / 2);

            let minDistance = Infinity;
            let targetVideo = null;

            videos.forEach(video => {
                const rect = video.getBoundingClientRect();
                // 计算视频的中心位置(Y轴坐标,相对于整个页面)
                const videoCenterY = window.scrollY + rect.top + (rect.height / 2);

                // 计算“视口中心”与“视频中心”的垂直距离(绝对值)
                const distance = Math.abs(viewportCenterY - videoCenterY);

                // 距离越近,越优先作为目标视频
                if (distance < minDistance) {
                    minDistance = distance;
                    targetVideo = video;
                }
            });

            return targetVideo;
        }

        function syncVideoStateToBtn() {
            const targetVideo = getTargetVideo();
            if (targetVideo) {
                isPlaying = !targetVideo.paused;
                updatePlayPauseBtnText();
            }
        }

        function togglePlayPause(video) {
            if (video.paused) {
                video.play();
                isPlaying = true;
            } else {
                video.pause();
                isPlaying = false;
            }
            updatePlayPauseBtnText();
        }

        function setVideoSpeed(speed) {
            IntPbr = speed;
            if (IntPbr > IntPbrMax) IntPbr = IntPbrMax;
            if (IntPbr < IntPbrMin) IntPbr = IntPbrMin;

            const targetVideo = getTargetVideo();
            if (targetVideo) {
                targetVideo.playbackRate = IntPbr;
                setMainBtnTxt();
                hideSpeedPanel();
            }
        }

        function updatePlayPauseBtnText() {
            playPauseBtn.textContent = isPlaying ? "暂停" : "播放";
        }

        function setMainBtnTxt() {
            mainBtn.innerHTML = "x" + IntPbr.toFixed(2);
        }

        function hideSpeedPanel() {
            speedPanel.classList.remove("show");
            speedPanel.classList.add("hidden");
        }

        // 监听滚动事件:用户滚动页面时,重新判断目标视频并同步按钮状态
        window.addEventListener('scroll', syncVideoStateToBtn);

        document.addEventListener('play', function (e) {
            if (e.target.tagName === 'VIDEO') {
                const currentVideo = getTargetVideo();
                if (currentVideo === e.target) {
                    isPlaying = true;
                    updatePlayPauseBtnText();
                }
            }
        }, true);

        document.addEventListener('pause', function (e) {
            if (e.target.tagName === 'VIDEO') {
                const currentVideo = getTargetVideo();
                if (currentVideo === e.target) {
                    isPlaying = false;
                    updatePlayPauseBtnText();
                }
            }
        }, true);
    }

    var sli = setInterval(() => {
        if (document.querySelector("video")) {
            Main();
            clearInterval(sli);
        }
    }, 1000);
    setTimeout(() => {
        clearInterval(sli);
    }, 10000);
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址