Greasy Fork is available in English.

后视镜

点击悬浮球开启摄像头视频悬浮窗,再次点击悬浮球关闭视频,鼠标滚轮缩放大小,左键按住拖动位置,双击视频隐藏,再次双击隐藏位置显示视频。

// ==UserScript==
// @name         后视镜
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  点击悬浮球开启摄像头视频悬浮窗,再次点击悬浮球关闭视频,鼠标滚轮缩放大小,左键按住拖动位置,双击视频隐藏,再次双击隐藏位置显示视频。
// @author       husky180
// @match        http://*/*
// @match        https://*/*
// @exclude      http://*/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';
    let hasVideo = false;
    let video = null;
    let cover = null;
    let videoStream = null;
    let isDragging = false;
    let offsetX = 0;
    let offsetY = 0;

    // 悬浮按钮
    var button = document.createElement('div');
    button.style.position = 'fixed';
    button.style.bottom = '20px';
    button.style.right = '20px';
    button.style.width = '50px';
    button.style.height = '50px';
    button.style.backgroundColor = 'rgba(0, 130, 220, 0.2)';
    button.style.borderRadius = '50%';
    button.style.cursor = 'pointer';
    button.style.textAlign = 'center';
    button.style.lineHeight = '50px';
    button.style.color = 'white';
    button.style.zIndex = '999999';
    button.textContent = 'Click';

    document.body.appendChild(button);

    // 悬浮按钮点击事件
    button.addEventListener('click', function() {
        if(hasVideo) {
            document.body.removeChild(video);
            document.body.removeChild(cover);
            hasVideo = false;
            video = null;
            cover = null;
            videoStream.getTracks().forEach(track => {
                track.stop();
            })
            return
        }
        hasVideo = true;
        video = document.createElement('video');
        video.id = 'huskyBackVideo';
        video.style.width = '300px';
        video.style.height = '300px';
        video.style.position = 'fixed';
        video.style.bottom = '200px';
        video.style.right = '200px';
        video.style.zIndex = '999998';
        video.autoplay = true;
        video.playsinline = true;
        // 获取摄像头视频流
        navigator.mediaDevices.getUserMedia({ video: true })
            .then(stream => {
                // 将视频流分配给 video 元素
                video.srcObject = stream;
                videoStream = stream;
            }).catch(error => {
            console.error('Error accessing the camera:', error);
        });
        document.body.appendChild(video);
        cover = document.createElement("div");
        cover.id = 'huskyBackVideoCover';
        cover.style.backgroundColor = 'rgba(255,255,255,0.5)';
        cover.style.width = '300px';
        cover.style.height = '300px';
        cover.style.zIndex = '999999';
        cover.style.position = 'fixed';
        cover.style.bottom = '200px';
        cover.style.right = '200px';
        document.body.appendChild(cover);
        cover.addEventListener("click", function(){
            if (isDragging) {
                return
            }
            if (video.style.width == "0px") {
                video.style.width = "300px";
            } else {
                video.style.width = "0px"
            }
        })
        cover.addEventListener('mousedown', function(event){
            isDragging = true;
            offsetX = event.clientX - video.offsetLeft;
            offsetY = event.clientY - video.offsetTop;
        })
        cover.addEventListener('mousemove', function(event) {
            if (isDragging) {
                // Update video element position based on mouse movement
                video.style.left = (event.clientX - offsetX) + 'px';
                video.style.top = (event.clientY - offsetY) + 'px';
                cover.style.left = (event.clientX - offsetX) + 'px';
                cover.style.top = (event.clientY - offsetY) + 'px';
            }
        });
        cover.addEventListener('mouseup', function() {
            setTimeout(() => {
                isDragging = false;
            }, 100)
        });
        // 监听鼠标滚轮事件
        cover.addEventListener('wheel', function(event) {
            // 阻止默认的滚轮事件,避免页面滚动
            event.preventDefault();

            // 判断滚轮方向
            const zoomSpeed = 0.1; // 缩放速度
            if (event.deltaY < 0) {
                // 向上滚动,放大
                video.style.width = (parseFloat(video.style.width) * (1 + zoomSpeed)) + 'px';
                video.style.height = (parseFloat(video.style.height) * (1 + zoomSpeed)) + 'px';
                cover.style.width = (parseFloat(cover.style.width) * (1 + zoomSpeed)) + 'px';
                cover.style.height = (parseFloat(cover.style.height) * (1 + zoomSpeed)) + 'px';
            } else {
                // 向下滚动,缩小
                video.style.width = (parseFloat(video.style.width) * (1 - zoomSpeed)) + 'px';
                video.style.height = (parseFloat(video.style.height) * (1 - zoomSpeed)) + 'px';
                cover.style.width = (parseFloat(cover.style.width) * (1 - zoomSpeed)) + 'px';
                cover.style.height = (parseFloat(cover.style.height) * (1 - zoomSpeed)) + 'px';
            }
        });
    });
})();