Greasy Fork

Greasy Fork is available in English.

哔哩哔哩画中画弹幕

哔哩哔哩画中画支持显示弹幕

当前为 2022-09-04 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         哔哩哔哩画中画弹幕
// @namespace    qwq0
// @version      0.15
// @description  哔哩哔哩画中画支持显示弹幕
// @author       QwQ~
// @match        https://www.bilibili.com/video/*
// @match        https://www.bilibili.com/medialist/play/*
// @match        https://www.bilibili.com/bangumi/play/*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        none
// ==/UserScript==

setTimeout(function() {
    'use strict';
    var videoHolder = null;
    var video = null;

    var width = 0;
    var height = 0;

    var canvas = document.createElement("canvas");
    var context = canvas.getContext("2d");
    var canvasWidth = canvas.width = 0;
    var canvasHeight = canvas.height = 0;
    var danmuFontsize = 0;
    var textCanvas = document.createElement("canvas");
    var textCanvasContext = textCanvas.getContext("2d");
    var nVideo = document.createElement("video");

    var timeoutId = 0;

    var danmuList = [];
    var danmuLine = [];
    var danmuCount = 0;

    var danmuHolder = null;
    async function addDanmu(text, color)
    {
        if(timeoutId && (danmuList.length <= 20 || Math.random() < 20 / danmuList.length))
        {
            danmuCount++;
            var lineNum = 0;
            for(var i=0;i < 5 && danmuLine[lineNum] + 6 >= danmuCount;i++)
            {
                lineNum = Math.floor(Math.random() * 12);
            }
            danmuLine[lineNum] = danmuCount;
            var textWidth = textCanvasContext.measureText(text).width;
            textCanvasContext.clearRect(0, 0, textWidth, danmuFontsize);
            textCanvasContext.fillStyle = color;
            textCanvasContext.fillText(text, 0, 0);
            danmuList.push({text: text, color: (color ? color : "rgb(255, 255, 255)"), x: canvasWidth, y: lineNum * danmuFontsize, w: textWidth,i: await createImageBitmap(textCanvas, 0, 0, textWidth, danmuFontsize) });
        }
    }
    var danmuObserver = new MutationObserver(e => {
        e.forEach(o=>{
            // console.log("danmu(all)", o);
            if(o.type == "childList")
            {
                o.addedNodes.forEach(ele =>{
                    if(ele.innerText)
                    {
                        let text = ele.innerText;
                        addDanmu(text, ele.style.color);
                        console.log("danmu(it)", ele.style.color, text);
                    }
                    else if(ele.textContent)
                    {
                        let text = ele.textContent;
                        addDanmu(text, o.target.style.color);
                        console.log("danmu(ct)", o.target.color, text);
                    }
                });
            }
        });
    });
    setInterval(()=>{
        if(videoHolder == null)
        {
            videoHolder = document.getElementsByClassName("bilibili-player-video")[0] || document.getElementsByClassName("bpx-player-video-wrap")[0];
            video = videoHolder.children[0];
            video.addEventListener("enterpictureinpicture",() => {
                if(!timeoutId)
                {
                    timeoutId = setTimeout(draw, Math.floor(1000 / 60));
                    setTimeout(()=>{
                        nVideo.requestPictureInPicture();
                        nVideo.play();
                    }, 250);
                }
                else
                {
                    nVideo.requestPictureInPicture();
                    nVideo.play();
                }
            });
            var style = document.createElement("style");
            style.innerText = `
            .bpx-player-ctrl-btn.bpx-player-ctrl-pip, .bilibili-player-video-btn.bilibili-player-video-btn-pip.closed
            {
                filter: drop-shadow(1px 1px 3px #49e3dc);
            }
            `;
            document.body.appendChild(style);
        }
        var nowDanmuHolder = document.getElementsByClassName("bilibili-player-video-danmaku")[0] || document.getElementsByClassName("bpx-player-row-dm-wrap")[0];
        if(nowDanmuHolder != danmuHolder || width != video.videoWidth || height != video.videoHeight)
        {
            danmuHolder = nowDanmuHolder;
            danmuObserver.disconnect();
            width = video.videoWidth;
            height = video.videoHeight;
            canvasWidth = canvas.width = (Math.min(height, width) < 700 ? width : Math.floor(width / 2));
            canvasHeight = canvas.height = (Math.min(height, width) < 700 ? height : Math.floor(height / 2));
            textCanvas.height = danmuFontsize = Math.floor(Math.min(canvasWidth, canvasHeight) / 14.5);
            textCanvas.width = danmuFontsize * 35;

            textCanvasContext.textBaseline = "top";
            textCanvasContext.shadowBlur = 3;
            textCanvasContext.shadowColor = "rgb(0, 0, 0)";
            textCanvasContext.font = danmuFontsize + 'px SimHei,"Microsoft JhengHei",Arial,Helvetica,sans-serif';

            nVideo.srcObject = canvas.captureStream(60);
            setTimeout(() => nVideo.play(), 1500);
            danmuObserver.observe(danmuHolder, { childList: true, subtree: true });
            console.log("[哔哩哔哩画中画弹幕]", "视频切换")
            console.log("[哔哩哔哩画中画弹幕]", "视频分辨率", width, height);
            console.log("[哔哩哔哩画中画弹幕]", "渲染分辨率", canvasWidth, canvasHeight);
        }
    }, 900);

    var lastTime = Date.now();
    function draw()
    {
        var nowTime = Date.now();
        var timeInterval = nowTime - lastTime;
        lastTime = nowTime;
        if(video)
        {
            context.globalAlpha = 1;
            context.drawImage(video, 0, 0, width, height, 0, 0, canvasWidth, canvasHeight);
            if(video.readyState >= 1)
            {
                context.globalAlpha = 0.7;
                danmuList.forEach(o => {
                    context.drawImage(o.i, Math.floor(o.x), Math.floor(o.y));
                    if(!video.paused)
                    {
                        o.x -= timeInterval * danmuFontsize * 0.0035;
                    }
                });
                danmuList = danmuList.filter(o => (o.x >= -o.w));
            }
            else
            {
                danmuList = [];
            }
        }
        timeoutId = setTimeout(draw, Math.floor(1000 / 60));
    }
    console.log("[哔哩哔哩画中画弹幕]", "已加载")
}, 500);