<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>悬浮摄像头</title>
    <style>
        body { margin: 0; padding: 0; }
        
        .floating-camera {
            position: fixed;       
            top: 20%;              
            left: 50%;             
            transform: translate(-50%, -50%); 
            width: 320px;          
            height: 240px;         
            min-width: 160px;      
            min-height: 120px;     
            border-radius: 12px;   
            overflow: hidden;      
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); 
            z-index: 999999;       
            background-color: #000;
            cursor: move;          
            user-select: none;     
        }

        video {
            width: 100%;
            height: 100%;
            object-fit: cover;     
            pointer-events: none;  
        }

        
        .resize-handle {
            position: absolute;
            background: rgba(255, 255, 255, 0.3);
            pointer-events: auto;  
        }

        
        .nw { top: 0; left: 0; width: 15px; height: 15px; cursor: nw-resize; }
        .ne { top: 0; right: 0; width: 15px; height: 15px; cursor: ne-resize; }
        .sw { bottom: 0; left: 0; width: 15px; height: 15px; cursor: sw-resize; }
        .se { bottom: 0; right: 0; width: 15px; height: 15px; cursor: se-resize; }

        
        .n { top: 0; left: 15px; right: 15px; height: 5px; cursor: n-resize; }
        .s { bottom: 0; left: 15px; right: 15px; height: 5px; cursor: s-resize; }
        .w { top: 15px; bottom: 15px; left: 0; width: 5px; cursor: w-resize; }
        .e { top: 15px; bottom: 15px; right: 0; width: 5px; cursor: e-resize; }
    </style>
</head>
<body>

    <!-- 悬浮摄像头容器 -->
    <div id="cameraWindow" class="floating-camera">
        <video id="cameraVideo" autoplay muted playsinline></video>
        <div class="resize-handle nw" data-dir="nw"></div>
        <div class="resize-handle ne" data-dir="ne"></div>
        <div class="resize-handle sw" data-dir="sw"></div>
        <div class="resize-handle se" data-dir="se"></div>
        <div class="resize-handle n" data-dir="n"></div>
        <div class="resize-handle s" data-dir="s"></div>
        <div class="resize-handle w" data-dir="w"></div>
        <div class="resize-handle e" data-dir="e"></div>
    </div>

    <script>
        // 1. 初始化摄像头
        const video = document.getElementById('cameraVideo');
        if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
            navigator.mediaDevices.getUserMedia({ video: true, audio: false })
                .then(stream => { video.srcObject = stream; })
                .catch(error => {
                    console.error("无法访问摄像头:", error);
                    alert("摄像头启动失败,请检查权限或设备连接。");
                });
        } else {
            alert("您的浏览器不支持调用摄像头功能。");
        }

        // 2. 获取 DOM 元素并初始化居中位置
        const cameraWindow = document.getElementById('cameraWindow');
        window.addEventListener('load', () => {
            const rect = cameraWindow.getBoundingClientRect();
            cameraWindow.style.left = rect.left + 'px';
            cameraWindow.style.top = rect.top + 'px';
            cameraWindow.style.transform = 'none';
        });

        let isDragging = false;
        let dragOffsetX = 0, dragOffsetY = 0;

        cameraWindow.addEventListener('mousedown', function(e) {
            if (e.target.classList.contains('resize-handle')) return; 
            
            isDragging = true;
            dragOffsetX = e.clientX - cameraWindow.offsetLeft;
            dragOffsetY = e.clientY - cameraWindow.offsetTop;
            
            document.addEventListener('mousemove', onDragMove);
            document.addEventListener('mouseup', onDragEnd);
        });

        function onDragMove(e) {
            if (!isDragging) return;
            let newX = e.clientX - dragOffsetX;
            let newY = e.clientY - dragOffsetY;

            const maxX = window.innerWidth - cameraWindow.offsetWidth;
            const maxY = window.innerHeight - cameraWindow.offsetHeight;
            newX = Math.max(0, Math.min(newX, maxX));
            newY = Math.max(0, Math.min(newY, maxY));

            cameraWindow.style.left = newX + 'px';
            cameraWindow.style.top = newY + 'px';
        }

        function onDragEnd() {
            isDragging = false;
            document.removeEventListener('mousemove', onDragMove);
            document.removeEventListener('mouseup', onDragEnd);
        }

        let isResizing = false;
        let currentDir = '';
        let startWidth = 0, startHeight = 0;
        let startX = 0, startY = 0;
        let startLeft = 0, startTop = 0;

        document.querySelectorAll('.resize-handle').forEach(handle => {
            handle.addEventListener('mousedown', function(e) {
                isResizing = true;
                currentDir = this.dataset.dir;
                
                // 记录初始状态
                startWidth = cameraWindow.offsetWidth;
                startHeight = cameraWindow.offsetHeight;
                startX = e.clientX;
                startY = e.clientY;
                startLeft = cameraWindow.offsetLeft;
                startTop = cameraWindow.offsetTop;
                
                e.stopPropagation(); 
                e.preventDefault();
                
                document.addEventListener('mousemove', onResizeMove);
                document.addEventListener('mouseup', onResizeEnd);
            });
        });

        function onResizeMove(e) {
            if (!isResizing) return;
            
            let deltaX = e.clientX - startX;
            let deltaY = e.clientY - startY;
            
            let newWidth = startWidth;
            let newHeight = startHeight;
            let newLeft = startLeft;
            let newTop = startTop;

            // 水平方向处理
            if (currentDir.includes('e')) {
                newWidth = startWidth + deltaX;
            } else if (currentDir.includes('w')) {
                newWidth = startWidth - deltaX;
                newLeft = startLeft + deltaX;
            }

            // 垂直方向处理
            if (currentDir.includes('s')) {
                newHeight = startHeight + deltaY;
            } else if (currentDir.includes('n')) {
                newHeight = startHeight - deltaY;
                newTop = startTop + deltaY;
            }

            // 应用最小尺寸限制
            if (newWidth < 160) {
                if (currentDir.includes('w')) newLeft -= (160 - newWidth);
                newWidth = 160;
            }
            if (newHeight < 120) {
                if (currentDir.includes('n')) newTop -= (120 - newHeight);
                newHeight = 120;
            }

            // 更新窗口样式
            cameraWindow.style.width = newWidth + 'px';
            cameraWindow.style.height = newHeight + 'px';
            cameraWindow.style.left = newLeft + 'px';
            cameraWindow.style.top = newTop + 'px';
        }

        function onResizeEnd() {
            isResizing = false;
            document.removeEventListener('mousemove', onResizeMove);
            document.removeEventListener('mouseup', onResizeEnd);
        }
    </script>
</body>
</html>

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐