B站视频缩放/移动

针对B站的浏览器脚本,能够自由调节播放器中的画面

// ==UserScript==
// @name         B站视频缩放/移动
// @namespace    https://www.bilitools.top/
// @version      1.0
// @description  针对B站的浏览器脚本,能够自由调节播放器中的画面
// @author       DBsan
// @license      MIT
// @supportURL   https://github.com/dbsan2333/bilibili-drag
// @match        https://www.bilibili.com/*
// @icon         https://www.bilibili.com/favicon.ico
// @grant        GM_addStyle
// @require      https://scriptcat.org/lib/513/2.1.0/ElementGetter.js#sha256=aQF7JFfhQ7Hi+weLrBlOsY24Z2ORjaxgZNoni7pAz5U=
// ==/UserScript==

GM_addStyle(`
    .bilidrag-restore-button{
        color: #fff;
        font-size: 1.4em;
        position: absolute;
        bottom: 120px;
        left: 20px;
        border: none;
        border-radius: 5px;
        background-color: #6669;
        padding: 0.6em 0.8em;
        cursor: pointer;
        transition: all 0.2s
    }
    .bilidrag-restore-button:hover{
        background-color: #666c;
    }
    .bpx-player-container[data-ctrl-hidden='true'] .bilidrag-restore-button{
        opacity: 0;
    }
`);

const transform = {
    // 画面变形参数
    scale: 1, // 缩放系数,1为100%
    translateX: 0, // 水平位移,以px为单位
    translateY: 0, // 垂直位移,以px为单位

    getValue() {
        // 生成style.transform的值
        return `translate(${this.translateX}px, ${this.translateY}px) scale(${this.scale})`
    },
    update(element) {
        // 更新画面
        element.style.transform = this.getValue()
    },
    restore(element) {
        // 还原画面
        this.scale = 1
        this.translateX = 0
        this.translateY = 0
        this.update(element)
    },
    isOriginal() {
        return this.scale === 1 && this.translateX === 0 && this.translateY === 0
    }
};


main()
async function main() {
    /** @type {[HTMLElement,HTMLVideoElement]} */
    const [playerElement, videoWrapElement, videoElement] = await elmGetter.get(['#bilibili-player .bpx-player-video-area', '#bilibili-player .bpx-player-video-wrap', '#bilibili-player video'])
    videoElement.style.transition = 'all 0.3s'

    // “还原画面”按钮
    const restoreButtonElement = document.createElement('button')
    restoreButtonElement.innerText = '还原画面'
    restoreButtonElement.style.display = 'none'
    restoreButtonElement.className = 'bilidrag-restore-button'
    restoreButtonElement.onclick = function () {
        transform.restore(videoElement)
        this.style.display = 'none'
    }
    playerElement.appendChild(restoreButtonElement)

    // 鼠标滚轮缩放
    videoWrapElement.addEventListener('wheel', (ev) => {
        if (ev.shiftKey) {
            ev.preventDefault()
            ev.stopPropagation()

            // 缩放比例
            if (ev.deltaY < 0) {
                // 放大
                if (transform.scale >= 2) {
                    transform.scale += 0.1
                } else if (transform.scale < 1) {
                    transform.scale += 0.025
                } else {
                    transform.scale += 0.05
                }
            } else if (ev.deltaY > 0) {
                // 缩小
                if (transform.scale <= 1) {
                    transform.scale = Math.max(transform.scale - 0.025, 0.1)
                } else if (transform.scale > 2) {
                    transform.scale -= 0.1
                } else {
                    transform.scale -= 0.05
                }
            }

            // 如果调整到原始大小,隐藏“还原画面”按钮
            if (transform.isOriginal()) {
                restoreButtonElement.style.display = 'none'
            } else {
                restoreButtonElement.style.display = 'block'
            }
            transform.update(videoElement)
        }
    });

    // 鼠标拖拽
    let isMouseDown = false // 鼠标是否按下
    videoWrapElement.addEventListener('mousedown', (ev) => {
        if (ev.button === 0 && ev.shiftKey) {
            videoElement.style.transition = 'none'
            videoWrapElement.style.cursor = 'grab'
            isMouseDown = true
            // 鼠标按下时阻止点击事件,防止与B站播放/暂停事件冲突
            videoWrapElement.addEventListener('click', stopPropagation)
        }
    })
    videoWrapElement.addEventListener('mouseup', (ev) => {
        isMouseDown = false
        videoElement.style.transition = 'all 0.3s'
        videoWrapElement.style.cursor = 'default'
        setTimeout(() => {
            // 鼠标松开时移除阻止点击事件的Listener(我没招了)
            videoWrapElement.removeEventListener('click', stopPropagation)
        }, 0);
    })
    videoWrapElement.addEventListener('mousemove', (ev) => {
        if (ev.shiftKey && isMouseDown) {
            transform.translateX += ev.movementX
            transform.translateY += ev.movementY

            transform.update(videoElement)
            restoreButtonElement.style.display = 'block'
        }
    })
}
function stopPropagation(ev) {
    ev.preventDefault()
    ev.stopPropagation()
}